Qual é a justificativa para tornar estáticos certos métodos para tipos de dados?
Em C#, por exemplo, existem métodos estáticos para saber se uma string é nula ou vazia , localizar um elemento em uma matriz, limpar uma matriz etc. métodos para copiar elementos entre arrays.
Da mesma forma, em Python existem funções para obter o comprimento de uma lista, para filtrar uma lista, etc., mas há métodos de instância para encontrar o índice de um elemento e para copiar a lista.
Ao contrário, em JavaScript, praticamente qualquer operação que eu precise fazer em um tipo de dados integrado estará acessível por meio de método ou propriedade de instância.
Então, qual é a justificativa para tornar certas coisas estáticas e outras não?
EDIT : Para ser claro, eu entendo o propósito dos métodos estáticos. Minha pergunta é mais sobre por que certos métodos são escritos como estáticos quando poderiam ter sido escritos como métodos de instância. Como Find
para matrizes e Format
strings em C# filter
e len
em Python.
Respostas
EDIT : a pergunta foi refinada, então vou refinar minha resposta, mas deixe a anterior abaixo para não tornar os comentários irrelevantes.
Pode haver várias razões para implementar um método como um método estático em vez de um método de instância, com diferentes linguagens ou estruturas possivelmente tendo diferentes fundamentos:
A interface pública de um objeto é um contrato. Se o seu
Array
tipo expõe umSort
método, todo array a partir de agora, independentemente do tipo ou versão da linguagem, precisa suportá-lo. Se, no entanto, anArray
for apenas uma representação de dados na memória com um contrato fino e a lógica for movida para uma classe comoArray.Sort
, isso lhe dará mais flexibilidade para estender ou modificar seu comportamento sem quebrar a compatibilidade.Veja esta citação do Código Limpo de Bob Martin :
Os objetos expõem o comportamento e ocultam os dados; A estrutura de dados expõe dados e não tem comportamento significativo"
Nesse caso, a List
classe é uma classe que expõe o comportamento - ela oculta seus dados (é implementada como uma matriz redimensionável, mas isso é um detalhe de implementação), mas expõe a funcionalidade. Um int[]
, no entanto, é uma estrutura de dados. int
É um bloco alocado sequencialmente . Ele tem uma Length
propriedade porque faz parte de seus dados , mas não tem muito comportamento - o comportamento é transferido para uma classe que opera nele.
- Pode ser uma escolha estilística. Python não é uma linguagem puramente orientada a objetos e pode não ter a garantia Java/C# "tudo é um objeto", então optou por usar funções de linguagem como
len()
essa que podem ser usadas independentemente de seu parâmetro ser uma instância de objeto ou não.
Resposta anterior
A lógica por trás dos métodos estáticos em C# é ter métodos vinculados a um tipo, mas não a uma instância . Pode haver várias razões para isso:
- Métodos que podem operar em uma instância e
null
, comostring.IsNullOrEmpty
, naturalmente não podem ser métodos de instância - porque pode não haver uma instância. - Da mesma forma, os métodos de fábrica que criam uma instância não têm uma instância a partir da qual sejam chamados. Por exemplo, o método estático obsoleto de alguém
WebRequest.Create()
retorna uma nova instância de um tipo derivado deWebRequest
. - Embora seja fácil esquecer, o .NET tem tratamento diferente para um tipo integral nativo (
int i =5
) e um tipo de referência em caixa em torno dele (object boxed = 5
). Se você chamar um método em um tipo de valor (int i = 5; t.ToString();
), incorrerá no custo de encaixotá-lo. É por isso que algumas operações não são definidas como um método em um objeto, mas como um método estático que aceita um valor. É por isso que operações matemáticas comoMath.Abs
não são definidas como métodos nos tipos de caixa, mas como funções estáticas que recebem e retornam valores. - Às vezes, métodos estáticos são usados para "configurar" um tipo, não uma instância específica, mas o comportamento desse tipo no sistema. Por exemplo,
[ServicePointManager.SetTcpKeepAlive()][1]
configura o comportamento de todas as classes que usam a pilha HTTP(s) compartilhada em .NET, para que você possa configurá-los todos por meio desses métodos estáticos.
Métodos estáticos fazem muito sentido
- quando o método está relacionado ao tipo, mas não requer uma instância existente; ou
- quando a linguagem oferece suporte a namespace apenas por meio de classes.
C# oferece suporte apenas a funções como membros de classes, portanto, as funções auxiliares devem ser escritas como métodos estáticos. A classe C# Math
é um excelente exemplo.
JavaScript não possui classes, mas toda variável não local está associada a algum objeto – no navegador, a maioria das APIs são acessíveis por meio do window
objeto. No entanto, existem métodos acessíveis por meio de um objeto semelhante a uma classe, por exemplo, Array.isArray(x)
.
O Python tem um design idiossincrático que é principalmente orientado a objetos, mas possui algumas funções internas como len()
ou iter()
, principalmente por razões históricas. Sob o capô, eles invocam métodos reais, por exemplo, iter(x)
geralmente is x.__iter__()
, com um fallback para objetos que podem ser indexados como uma lista.
Mais interessantes são os métodos que static
não são por peculiaridades da linguagem, mas porque fazem sentido. Os métodos do tipo construtor ou fábrica são o exemplo clássico em que não temos um objeto existente no qual poderíamos chamar um método. Algumas funções também não operam em exatamente um objeto, por exemplo, String.IsNullOrEmpty(x)
operam em zero ou um objeto. Isso não funciona como um método, por exemplo x.IsNullOrEmpty()
, lançaria uma exceção se fosse nulo! O Array.isArray(x)
método em JS é outro ótimo exemplo: queremos chamar isArray(x)
objetos de qualquer tipo, não apenas arrays. Assim, um método de instância não funciona a menos que faça parte do protótipo de cada objeto.