Classe com método único – melhor abordagem?

Digamos que eu tenha uma aula destinada a realizar uma única function. Depois de executar a function, ela pode ser destruída. Existe alguma razão para preferir uma dessas abordagens?

// Initialize arguments in constructor MyClass myObject = new MyClass(arg1, arg2, arg3); myObject.myMethod(); // Pass arguments to method MyClass myObject = new MyClass(); myObject.myMethod(arg1, arg2, arg3); // Pass arguments to static method MyClass.myMethod(arg1, arg2, arg3); 

Eu estava sendo intencionalmente vago sobre os detalhes, para tentar obter orientações para diferentes situações. Mas eu realmente não tinha em mente funções de biblioteca simples como Math.random (). Estou pensando em mais classs que executam alguma tarefa específica e complexa, mas requerem apenas um método (público) para fazê-lo.

Eu costumava amar classs utilitárias cheias de methods estáticos. Eles fizeram uma grande consolidação de methods auxiliares que, de outra forma, estariam por aí causando um inferno de redundância e manutenção. Eles são muito fáceis de usar, sem instanciação, sem descarte, apenas fogo e esquecimento. Eu acho que esta foi a minha primeira tentativa inconsciente de criar uma arquitetura orientada a serviços – muitos serviços sem estado que apenas fizeram seu trabalho e nada mais. Como um sistema cresce no entanto, os dragões estão chegando.

Polimorfismo
Digamos que temos o método UtilityClass.SomeMethod que vibra alegremente. De repente, precisamos mudar um pouco a funcionalidade. A maior parte da funcionalidade é a mesma, mas temos que alterar algumas partes, no entanto. Se não fosse um método estático, poderíamos criar uma class derivada e alterar o conteúdo do método conforme necessário. Como é um método estático, não podemos. Claro, se precisarmos apenas adicionar funcionalidade antes ou depois do método antigo, podemos criar uma nova class e chamar a antiga dentro dela – mas isso é nojento.

Problemas de interface
Métodos estáticos não podem ser definidos por meio de interfaces por motivos lógicos. E como não podemos replace methods estáticos, as classs estáticas são inúteis quando precisamos passá-las pela interface. Isso nos torna incapazes de usar classs estáticas como parte de um padrão de estratégia. Podemos corrigir alguns problemas passando delegates em vez de interfaces .

Testando
Isso basicamente anda de mãos dadas com as dificuldades de interface mencionadas acima. Como nossa capacidade de intercambiar implementações é muito limitada, também teremos problemas para replace o código de produção pelo código de teste. Mais uma vez, podemos envolvê-los, mas isso exigirá que alteremos grandes partes do nosso código apenas para aceitar invólucros em vez dos objects reais.

Foster blobs
Como os methods estáticos são normalmente usados ​​como methods utilitários e os methods utilitários normalmente terão propósitos diferentes, nós rapidamente acabaremos com uma class grande preenchida com funcionalidade não-coerente – idealmente, cada class deve ter uma única finalidade dentro do sistema. Eu prefiro ter cinco vezes as aulas, desde que seus objectives sejam bem definidos.

Parameter creep
Para começar, esse pequeno e inocente método estático pode ter um único parâmetro. À medida que a funcionalidade cresce, alguns novos parâmetros são adicionados. Em breve outros parâmetros são adicionados, que são opcionais, portanto, criamos sobrecargas do método (ou apenas adicionamos valores padrão, em idiomas que os suportam). Em pouco tempo, temos um método que leva 10 parâmetros. Apenas os três primeiros são realmente necessários, os parâmetros 4-7 são opcionais. Mas se o parâmetro 6 for especificado, é necessário preencher 7-9 também … Se tivéssemos criado uma class com o único objective de fazer o que esse método estático fazia, poderíamos resolver isso observando os parâmetros necessários no construtor e permitindo que o usuário defina valores opcionais por meio de propriedades ou methods para definir vários valores interdependentes ao mesmo tempo. Além disso, se um método tiver atingido essa complexidade, provavelmente ele precisará estar em sua própria class.

Consumidores exigentes para criar uma instância de classs sem motivo
Um dos argumentos mais comuns é, por que exigir que os consumidores de nossa class criem uma instância para invocar esse método único, sem ter mais uso para a instância depois? Criar uma instância de uma class é uma operação muito barata na maioria das linguagens, portanto a velocidade não é um problema. Adicionar uma linha extra de código ao consumidor é um custo baixo para estabelecer a base de uma solução muito mais sustentável no futuro. E, finalmente, se você quiser evitar a criação de instâncias, basta criar um wrapper singleton de sua class que permita uma fácil reutilização – embora isso faça com que o requisito de que sua class seja stateless. Se não for stateless, você ainda pode criar methods wrapper estáticos que lidam com tudo, enquanto ainda oferece todos os benefícios a longo prazo. Finalmente, você também pode criar uma class que oculte a instanciação como se fosse um singleton: MyWrapper.Instance é uma propriedade que retorna apenas MyClass ();

Apenas um Sith lida em absolutos
Claro, há exceções para o meu desagrado de methods estáticos. Classes de utilitários verdadeiras que não representam risco para inchar são casos excelentes para methods estáticos – System.Convert como um exemplo. Se o seu projeto é único, sem requisitos para manutenção futura, a arquitetura geral realmente não é muito importante – estática ou não estática, realmente não importa -, entretanto, a velocidade de desenvolvimento.

Padrões, padrões e padrões!
O uso de methods de instância não inibe você de usar methods estáticos e vice-versa. Contanto que haja um raciocínio por trás da diferenciação e seja padronizado. Não há nada pior do que investigar uma camada de negócios alastrando com diferentes methods de implementação.

Eu prefiro o modo estático. Como a class não está representando um object, não faz sentido criar uma instância dela.

Classes que existem apenas para seus methods devem ser deixadas estáticas.

Se não houver razão para ter uma instância da class criada para executar a function, use a implementação estática. Por que os consumidores dessa class criam uma instância quando não é necessário.

Se você não precisa salvar o estado do object, não há necessidade de instanciá-lo em primeiro lugar. Eu iria com o único método estático que você passa parâmetros para.

Eu também alertaria contra uma class gigante de Utils que tem dúzias de methods estáticos não relacionados. Isso pode ficar desorganizado e pesado com pressa. É melhor ter muitas classs, cada uma com poucos methods relacionados.

Eu realmente não sei qual é a situação aqui, mas eu olharia para colocá-lo como um método em uma das classs que arg1, arg2 ou arg3 pertencem – Se você pode dizer semanticamente que uma daquelas classs seria dona do método.

Eu diria que o formato do Método Estático seria a melhor opção. E eu também tornaria a class estática, assim você não teria que se preocupar em criar acidentalmente uma instância da class.

Eu sugiro que é difícil responder com base nas informações fornecidas.

Meu instinto é que, se você vai ter apenas um método, e você vai jogar a class fora imediatamente, então faça dela uma class estática que toma todos os parâmetros.

Claro, é difícil dizer exatamente por que você precisa criar uma única class apenas para este método. É a típica situação de “Utilities class”, como a maioria está assumindo? Ou você está implementando algum tipo de class de regras, da qual pode haver mais no futuro.

Por exemplo, essa class pode ser plugável. Em seguida, você deseja criar uma interface para o seu método e, em seguida, deseja ter todos os parâmetros passados ​​na interface, em vez de inseri-los no construtor, mas não deseja que ele seja estático.

Sua aula pode ficar estática?

Se assim for, então eu faria uma class ‘Utilities’ que eu colocaria todas as minhas classs de function única.

Se esse método é sem estado e você não precisa passá-lo, então faz mais sentido defini-lo como estático. Se você precisar passar o método, considere usar um delegado em vez de uma das outras abordagens propostas.

Para aplicativos simples e ajudantes internal , usaria um método estático. Para aplicativos com componentes, estou adorando o Managed Extensibility Framework . Aqui está um trecho de um documento que estou escrevendo para descrever os padrões que você encontrará nas minhas APIs.

  • Serviços
    • Definido por uma interface de I[ServiceName]Service .
    • Exportado e importado pelo tipo de interface.
    • A implementação única é fornecida pelo aplicativo host e consumida internamente e / ou por extensões.
    • Os methods na interface de serviço são thread-safe.

Como um exemplo planejado:

 public interface ISettingsService { string ReadSetting(string name); void WriteSetting(string name, string value); } [Export] public class ObjectRequiringSettings { [Import] private ISettingsService SettingsService { get; set; } private void Foo() { if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString) { // whatever } } } 

Eu faria tudo no construtor. igual a:

 new MyClass(arg1, arg2, arg3);// the constructor does everything. 

ou

 MyClass my_object(arg1, arg2, arg3); 

Uma questão mais importante a ser considerada é se o sistema estaria sendo executado em um ambiente multithread e se seria thread-safe ter um método estático ou variables ​​…

Você deve prestar atenção ao estado do sistema.

Você pode evitar a situação todos juntos. Tente refatorar para obter arg1.myMethod1(arg2, arg3) . Troque arg1 com arg2 ou arg3 se fizer mais sentido.

Se você não tem controle sobre a class de arg1, decore-o:

 class Arg1Decorator private final T1 arg1; public Arg1Decorator(T1 arg1) { this.arg1 = arg1; } public T myMethod(T2 arg2, T3 arg3) { ... } } arg1d = new Arg1Decorator(arg1) arg1d.myMethod(arg2, arg3) 

O raciocínio é que, em OOP, dados e methods processam esses dados juntos. Além disso, você obtém todas as vantagens mencionadas por Mark.

Eu acho que, se as propriedades de sua class ou a instância de class não serão usadas em construtores ou em seus methods, os methods não são sugeridos para serem projetados como padrão ‘estático’. O método estático deve sempre ser pensado como “ajuda”.