É um método "iniciar", "executar" ou "executar" uma boa prática?

30

Atualmente, estou trabalhando em uma base de código que possui muitas classes que implementam um método Start. Parece-me uma construção em duas fases, que eu sempre considerara uma prática ruim. Não sei dizer a diferença entre isso e um construtor.

Quando é apropriado usar um método start em vez da construção normal do objeto?

Quando devo preferir usar o construtor?

Edit: Eu não acho que seja relevante, mas a linguagem de programação é C #, poderia ser aplicada igualmente a Java ou C ++

Dave Hillier
fonte
3
Você poderia adicionar um pouco mais de contexto? Língua? Rosca vs rosca única? diferença entre starte o construtor? etc ...
@ MichaelT Eu posso entender por que você está perguntando, mas estou interessado no princípio geral por trás de quando é apropriado. Estou preocupado que, se eu desse exemplos específicos da base de código em que estou trabalhando, as respostas seriam muito focadas nos detalhes e não nas minhas perguntas específicas.
23613 Dave Hillier
2
@DaveHillier Por exemplo, em perl, é prática comum (e boa) ter um initmétodo de algum tipo fora da newfunção - perldoc.perl.org/perlobj.html . Os idiomas de um idioma podem funcionar bem lá e não em outros idiomas.
11
Exemplos de classes com Startmétodos em APIs comuns incluem threads e cronômetros.
23413 luiscubal
11
Conte-me entre aqueles que precisam de um exemplo de código para entender o que você está realmente perguntando.
user16764

Respostas:

44

Um Start()método (como Run(), Execute()ou algo semelhante) é apropriado quando o custo de construção do objecto é baixa, mas o custo de utilização é elevada. Por exemplo: Uma classe que encapsula um algoritmo de otimização do melhor caminho. É trivial configurá-lo com um conjunto de parâmetros ( Xquadrados por Yquadrados, com esse método de avaliação), mas pode demorar um pouco para ser executado. Se você deseja criar 20 desses objetos, pode atrasar a execução até que todos eles tenham sido criados - isso permite paralelizá-los mais facilmente, por exemplo.

Como alternativa, pode ser útil quando você não sabe quando o objeto será necessário para iniciar - talvez porque seja baseado na entrada do usuário ou na lógica que seleciona em uma lista de possibilidades.

Isso pressupõe, é claro, que Start()é o método útil no objeto, e não equivalente a um Initialize()método. Se é apenas uma maneira extra de definir mais parâmetros, ela não deveria existir.

Bobson
fonte
11
Outro uso para um método de inicialização é quando a ação executada também cria um novo thread de trabalho ou um cronômetro. O construtor não deve fazer esse tipo de levantamento pesado ou gerar efeitos colaterais significativos (como criar um novo encadeamento).
Ziv
50

O Code Complete (e muitos outros recursos de engenharia de software) enfatiza a correspondência de suas classes com objetos do mundo real. Acredito que a razão fundamental para isso é que torna mais provável que você tenha uma compreensão verdadeira do que está implementando, em vez de se esquivar de uma ideia intangível.

Se você é assinante dessa teoria, não vejo nada de errado em adicionar um Start()método a qualquer classe que, se fosse um objeto real, também tivesse um estado de repouso. Se não faz sentido que seu objeto exista enquanto não está em execução (ou não faz sentido que ele esteja em execução), então eu diria que é uma má prática.

Dan Albert
fonte
16
Uma boa analogia. Vale a pena notar que isso Start()poderia corresponder a um botão liga / desliga (como um interruptor de luzes) que deveria ter um Stop(), ou a um botão de pressão (como o botão Imprimir em uma copiadora), onde ele inicia e é executado até terminar.
23413 Bobson
3
+1 bem dito e bem-vindo ao P.SE, respostas como esta são um ótimo começo.
Jimmy Hoffa
14

Você pode usar a inicialização lenta.

Na programação de computadores, a inicialização lenta é a tática de atrasar a criação de um objeto, o cálculo de um valor ou algum outro processo caro até a primeira vez que for necessário.

Dessa forma, você evita o acoplamento temporal, o que significa que o consumidor de sua classe precisa chamar certos métodos em uma certa ordem. Ter que ligar start()primeiro é uma maneira de saber como a classe funciona internamente, o que é ruim, porque você pode mudar isso no futuro.

Atraso na inicialização cara até que seja necessário primeiro.

Exemplo:

public class FooClass{

    private ExpensiveResource resource;
    private CheapResource cheap;

    public  FooClass(String someParameter){
        // constructor: initialize CheapResource cheap 
            // but NOT ExpensiveResource resource
    }

    public ExpensiveResource getExpensiveResource(){
        if (resource == null) {
            this.initializeExpensiveResource();     
        }
        return this.resource
    }

    public String getExpensiveResourceName(){
        if (resource == null) {
            this.initializeExpensiveResource();     
        }
        return this.resource.getName();
    }   

    public CheapResource getCheapResource(){
        return this.cheap;
    }

    private initializeExpensiveResource(){
        // do expensive initialization of field "resource"
    }

}

public class Test{
    public static void main (String args[]){

        FooClass foo = new FooClass("some string");
        CheapResource cr = foo.getCheapResource();
        String s = foo.getExpensiveResourceName(); 
          // just now is the expensive resource initialized

    }
}
Tulains Córdova
fonte
5
Outro benefício da inicialização lenta que vale a pena notar é que é preciso muito pouco esforço para transformá-la em um proxy virtual . Dependendo da situação, isso pode ser muito útil para exibir algo enquanto aguarda o carregamento de um recurso (especialmente útil para coisas como imagens remotas). Com base na pergunta original, não acho que seja isso que o OP estava buscando, mas achei que valeria a pena mencionar.
Dan Albert
@DanAlbert você está certo não é o que eu estava pedindo, mas ainda interessante
Dave Hillier