Pergunta

Eu estou tentando comparar compareCriteria. as simples como 'entre' e 'inArray' ou 'greaterThan'. Eu uso polimorfismo para essas classes. Um método que eles compartilham a partir da interface compareCriteria é 'matchCompareCriteria'.

O que eu estou tentando evitar é ter cada cheque classe para o tipo de compareCriteria eles devem corresponder contra. Por exemplo, o objeto inArray irá verificar se matchCompareCriteria é passado um objeto inArray, se não ele vai retornar falso, no caso, é que sabe como comparar.

Talvez instanceof é perfeitamente legítimo neste caso (os objetos sabem sobre si mesmos), mas ainda estou olhando para possíveis maneiras de evitá-lo. Alguma idéia?

exemplo pseudo-código:

betweenXandY = create new between class(x, y)
greaterThanZ = create new greaterThan class(z)
greaterThanZ.matchCompareCriteria(betweenXandY)

Se X e Y são maiores do que Z ele retornará verdadeiro.

edit:

1) instanceof é o que eu vejo, por agora, conforme necessário no método matchCompareCriteria. Eu gostaria de se livrar dele

2) verificações matchCompareCritera se um compareCriteria é contido por outro. Se todos os valores possíveis de um é contido por outro ele retorna true. Para muitas combinações de compareCriteria ele doens't até fazer sentido para compará-los para que eles retornar falso (como betweenAlfa e betweenNum seria incompatível).

Foi útil?

Solução

O problema que você está descrevendo é chamado . O nome vem do fato de que você precisa decidir qual pedaço de código para executar (expedição) com base nos tipos de dois objetos (daí: duplos).

Normalmente, em OO não é única expedição -. Chamando um método em um objeto faz com que a implementação desse objeto do método para executar

No seu caso, você tem dois objetos, e a implementação a ser executada depende dos tipos de ambos os objetos. Fundamentalmente, existe um acoplamento implícito por este que "se sente mal" quando você anteriormente só lidou com situações OO padrão. Mas não é realmente errado -. É apenas ligeiramente fora do domínio do problema do que as características básicas de OO são adequados directamente para a resolução

Se você estiver usando uma linguagem dinâmica (ou uma linguagem digitada-estático com a reflexão, o que é suficiente dinâmica para esta finalidade), você pode implementar isso com um método despachante em uma classe base. Em pseudo-código:

class OperatorBase
{
    bool matchCompareCriteria(var other)
    {
        var comparisonMethod = this.GetMethod("matchCompareCriteria" + other.TypeName);
        if (comparisonMethod == null)
            return false;

        return comparisonMethod(other);
    }
}

Aqui eu estou imaginando que a linguagem tem um built-in método em cada classe chamada GetMethod que me permite olhar para cima um método pelo nome, e também uma propriedade TypeName em cada objeto que me recebe o nome do tipo de o objeto. Então, se a outra classe é um GreaterThan, ea classe derivada tem um método chamado matchCompareCriteriaGreaterThan, vamos chamar esse método:

class SomeOperator : Base
{
    bool matchCompareCriteriaGreaterThan(var other)
    {
        // 'other' is definitely a GreaterThan, no need to check
    }
}

Assim, você só tem que escrever um método com o nome correto eo envio ocorre.

Em uma linguagem de tipagem estática esse método suportes sobrecarga por tipo de argumento, podemos evitar ter que inventar uma convenção de nomenclatura concatenado - por exemplo, aqui está em C #:

class OperatorBase
{
    public bool CompareWith(object other)
    {
        var compare = GetType().GetMethod("CompareWithType", new[] { other.GetType() });
        if (compare == null)
            return false;

        return (bool)compare.Invoke(this, new[] { other });
    }
}

class GreaterThan : OperatorBase { }
class LessThan : OperatorBase { }

class WithinRange : OperatorBase
{
    // Just write whatever versions of CompareWithType you need.

    public bool CompareWithType(GreaterThan gt)
    {
        return true;
    }

    public bool CompareWithType(LessThan gt)
    {
        return true;
    }
}

class Program
{
    static void Main(string[] args)
    {
        GreaterThan gt = new GreaterThan();
        WithinRange wr = new WithinRange();

        Console.WriteLine(wr.CompareWith(gt));
    }
}

Se você fosse para adicionar um novo tipo para o seu modelo, você precisa olhar para cada tipo anterior e perguntar se eles precisam interagir com o novo tipo de alguma forma. Consequentemente todas tipo tem que definir uma forma de interagir com todos os outros tipo - mesmo se a interação é alguns realmente simples padrão (como "não fazer nada exceto true retorno"). Mesmo que a inadimplência simples representa uma escolha deliberada você tem que fazer. Esta é disfarçado pela conveniência de não ter que escrever explicitamente qualquer código para o caso mais comum.

Por isso, pode fazer mais sentido para capturar as relações entre todos os tipos em uma tabela externa, em vez de espalhá-lo em torno de todos os objetos. O valor de centralizar será que você pode ver imediatamente se você perdeu todas as interações importantes entre os tipos.

Assim, você poderá ter um dicionário / map / hashtable (seja lá o que é chamado em seu idioma) que mapeia um tipo para outro dicionário. A segunda dicionário mapeia um segundo tipo para a função de comparação certo para esses dois tipos. A função geral CompareWith usaria essa estrutura de dados para procurar a função de comparação direito de chamada.

Qual abordagem é certo vai depender de quantos tipos é provável que você acabar com no seu modelo.

Outras dicas

Uma vez que você faz referência instanceof, estou assumindo que estamos trabalhando em Java aqui. Isso pode deixá-lo fazer uso de sobrecarga. Considere uma interface chamada SomeInterface, que tem um único método:

public interface SomeInterface {
    public boolean test (SomeInterface s);
}

Agora, definimos duas classes (inteligentemente nomeados) que implementam SomeInterface: Some1 e Some2. Some2 é chato: test sempre retorna false. Mas Some1 substitui a função test quando é dada uma Some2:

public class Some1 implements SomeInterface {
    public boolean test (SomeInterface s) {
        return false;
    }

    public boolean test (Some2 s) {
        return true;
    }
}

Isso nos permite evitar ter linha após linha de if para fazer a verificação de tipo. Mas há uma ressalva. Considere este código:

Some1 s1 = new Some1 ();
Some2 s2 = new Some2 ();
SomeInterface inter = new Some2 ();

System.out.println(s1.test(s2));     // true
System.out.println(s2.test(s1));     // false
System.out.println(s1.test(inter));  // false

Veja que o terceiro teste? Embora inter é do tipo Some2, ele é tratado como uma SomeInterface vez. Resolução de sobrecarga é determinado em tempo de compilação em Java, o que poderia torná-lo completamente inútil para você.

que o coloca de volta em uma praça: usando instanceof (que é avaliada em tempo de execução). Mesmo se você fizer isso dessa forma, ainda é um projeto ruim. Cada uma das suas classes tem de saber sobre todos os outros. Se você decidir adicionar outro, você tem que voltar para todas as existentes para adicionar funcionalidade para lidar com a nova classe. Este fica terrivelmente insustentável com pressa, que é um bom sinal de que o projeto é ruim.

A reformulação está em ordem, mas sem muito mais informação, eu não posso dar-lhe um bom empurrão particluarly na direção certa.

Você precisa criar uma super classe ou interface chamada Critérios. Em seguida, cada sub classe concreta vai implementar a interface Critérios. entre, greaterthan etc são critérios.

a classe Criteria irá especificar o método matchCompareCriteria que aceita um critério. A lógica real irá residir nas sub classes.

Você está procurando quer o padrão de estratégia de design ou o padrão de modelo de design.

Se eu entendi bem, o seu método baseia-se no tipo de verificação. Isso é muito difícil de evitar, e polimorfismo falhar em resolver o problema. A partir do seu exemplo, inArray precisa para verificar o tipo de parâmetro porque o comportamento do método depende sobre este assunto. Você não pode fazer isso por polimorfismo, ou seja, você não pode colocar um método polimórfico em suas classes para lidar com este caso. Isso é porque seu matchCompareCriteria depende do tipo do parâmetro, em vez de sua comportamento .

A regra de não usar instanceof é válido quando você verificar o tipo de um objeto para escolher qual o comportamento a ter. Claramente, que o comportamento pertence sobre os diferentes objetos cujo tipo você verificar. Mas neste caso, o comportamento do o objeto depende do tipo do objeto que são passados ??de que pertence sobre o objeto chamando, não sobre os chamados queridos como antes. O caso é semelhante a quando você substituir equals(). Você faz uma verificação de tipo para que o objeto passado é do mesmo tipo que objeto this e, em seguida, implementar o seu comportamento: se o teste falhar, return false; Caso contrário, faça os testes de igualdade.

Conclusão:. Utilizando instanceof é ok neste caso

Aqui está um longo artigo de Steve Yegge, o que explica melhor, Eu acho que, usando um exemplo simples e direto. Eu acho que isso mapeia grande para o seu problema.

Lembre-se: O polimorfismo é bom, exceto quando não é . :)

A abordagem Smalltalk seria introduzir mais camadas à hierarquia. Então entre e greaterThan seria subclasses de rangedCompareCriteria (ou algo assim), e rangeCompareCriteria :: matchCompareCriteria voltaria < strong> true quando perguntado se duas instâncias de si mesmo eram comparáveis.

Falando nisso, você provavelmente vai querer mudar o nome de "matchCompareCriteria" a algo que expressa a uma intenção pouco melhor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top