Vantagens e desvantagens de maneiras diferentes usando os tipos Either [closed]

5

Estou escrevendo software para compilar programas. Portanto, tenha um Compiler que compila um determinado código fonte. Em seguida, ele retorna um CompileResult semelhante a um tipo Either (na verdade, ele está delegando internamente para um objeto Either ). Agora pode haver dois casos (não excepcionais):

  1. A compilação é bem-sucedida ( CompileSuccess )
  2. A compilação falhou ( CompileFailure )

Vamos supor que a compilação tenha sido bem-sucedida. Eu já vi duas ou talvez três maneiras de lidar com isso:

  1. return new CompileResult( new CompileSuccess("success info") ) em que CompileResult contém CompileSuccess .
  2. return new CompileSuccess("success info") onde CompileSuccess implementa CompileResult .
  3. CompileResult.from( CompileSuccess("success info") ) , possivelmente implementado para retornar um objeto CompileResult ou um objeto CompileSuccess (como em 2.)

Existem outras maneiras de realizar o que eu quero fazer? Eu vi o 2. "herança" no Google goavas e em Scalas. Na goiaba, o 3. caminho é usado para o tipo Option . Quais são as diferenças e quando escolher o quê?

    
por valenterry 11.11.2014 / 17:22
fonte

1 resposta

-1

O principal problema com suas duas primeiras soluções é acoplar a fase de compilação e a criação / instanciação do resultado. Sua terceira maneira adiciona uma dependência de sua classe com uma maneira única de criar o resultado (CompileResult). Vamos resolver o primeiro ponto (delegando a criação de resultados):

class SuccessDetails {
    private String description;
    public SuccessDetails (String description) {
        this.description = description;
    }
}
class FailureDetails {
    private String description;
    public FailureDetails (String description) {
        this.description = description;
    }
}
class FailResultDetailed extends Result {
    private FailureDetails details;
    public FailResultDetailed (FailureDetails details) { this.details = details; }
}
class SuccessResultDetailed extends Result {
    private SuccessDetails details;
    public SuccessResultDetailed (SuccessDetails details) { this.details = details; }
}
class Result {}
class SuccessResult extends Result {
    private String description;
    public SuccessResult (String description) { this.description = description; }
}
class FailResult extends Result {
    private String description;
    public FailResult (String description) { this.description = description; }
}
interface IResultFactory {
    public SuccessResultDetailed success (SuccessDetails details);
    public FailResultDetailed fail (FailureDetails details);
}
class ResultFactory implements IResultFactory {
    public ResultFactory () { }
    public SuccessResultDetailed success (SuccessDetails details) { return new SuccessResultDetailed(details); }
    public FailResultDetailed fail (FailureDetails details) { return new FailResultDetailed(details); }
}
class Compiler {
    private IResultFactory resultFactory;
    public Compiler (IResultFactory resultFactory) { this.resultFactory = resultFactory; }
    public Result compile (String code, Boolean result) {
        if (result)
            return resultFactory.success(new SuccessDetails("Ok"));
        else
            return resultFactory.fail(new FailureDetails("You've made a mistake"));
    }
}

Agora, você está livre para mudar a maneira como manipula e cria o objeto Result, mas talvez queira adicionar alguns detalhes. A maneira mais intuitiva de resolvê-lo (para um desenvolvedor OO) é extrair os detalhes em um monte de outras classes:

class SuccessWithWarningsDetails extends SuccessDetails {
    private int[] lines;
    public SuccessWithWarningsDetails (String description, int[] lines) {
        super(description);
        this.lines = lines;
    }
}
class CompilerThatThrowsWarnings {
    private IResultFactory resultFactory;
    public CompilerThatThrowsWarnings (IResultFactory resultFactory) { this.resultFactory = resultFactory; }
    public Result compile (String code, Boolean result) {
        if (result)
            return resultFactory.success(new SuccessWithWarningsDetails("Ok", new int[] {1, 2, 3}));
        else
            return resultFactory.fail(new FailureDetails("You've made a mistake"));
    }
}

Mas, novamente, juntamos a fase de compilação e a criação / instanciação do resultado. Podemos tentar aplicar o mesmo processo que fizemos pela primeira vez:

interface IResultDetailsFactory {
    public SuccessDetails success (String description);
    public SuccessWithWarningsDetails successWithWarnings (String description, int[] lines);
    public FailureDetails failure (String description);
}
class ResultDetailsFactory implements IResultDetailsFactory{
    public ResultDetailsFactory () { }
    public SuccessDetails success (String description) { return new SuccessDetails(description); }
    public SuccessWithWarningsDetails successWithWarnings (String description, int[] lines) { return new SuccessWithWarningsDetails(description, lines); }
    public FailureDetails failure (String description) { return new FailureDetails(description); }
}
class CompilerThatThrowsWarningsWithoutCreation {
    private IResultFactory resultFactory;
    private IResultDetailsFactory resultDetailsFactory;
    public CompilerThatThrowsWarningsWithoutCreation (IResultFactory resultFactory, IResultDetailsFactory resultDetailsFactory) {
        this.resultFactory = resultFactory;
        this.resultDetailsFactory = resultDetailsFactory;
    }
    public Result compile (String code, Boolean result) {
        if (result)
            return resultFactory.success(resultDetailsFactory.successWithWarnings("Ok", new int[] {1, 2, 3}));
        else
            return resultFactory.fail(resultDetailsFactory.failure("You've made a mistake"));
    }
}

Estamos tão perto do final, se apenas não tivéssemos a depuração resultFactory ... Por quê? porque nos preocupamos apenas em produzir Resultado, não em como eles são organizados.

interface IResultDetailsAndResultFactory {
    public SuccessResultDetailed success (String description);
    public SuccessResultDetailed successWithWarnings (String description, int[] lines);
    public FailResultDetailed failure (String description);
}
class ResultDetailsAndResultFactory implements IResultDetailsAndResultFactory {
    private IResultFactory resultFactory;
    public ResultDetailsAndResultFactory (IResultFactory resultFactory) { this.resultFactory = resultFactory; }
    public SuccessResultDetailed success (String description) { return resultFactory.success(new SuccessDetails(description)); }
    public SuccessResultDetailed successWithWarnings (String description, int[] lines) { return resultFactory.success(new SuccessWithWarningsDetails(description, lines)); }
    public FailResultDetailed failure (String description) { return resultFactory.fail(new FailureDetails(description)); }
}
class CompilerThatThrowsWarningsWithoutCreationAndResult {
    private IResultDetailsAndResultFactory resultFactory;
    public CompilerThatThrowsWarningsWithoutCreationAndResult  (IResultDetailsAndResultFactory resultFactory) {
        this.resultFactory = resultFactory;
    }
    public Result compile (String code, Boolean result) {
        if (result)
            return resultFactory.successWithWarnings("Ok", new int[] {1, 2, 3});
        else
            return resultFactory.failure("You've made a mistake");
    }
}

Agora, temos não apenas uma fase de compilação que depende apenas de uma fábrica para criar resultados detalhados, mas também temos resultados que dependem apenas de uma fábrica para lidar com a maneira como eles serão processados.

    
por 10.02.2015 / 23:53
fonte