Страницы

вторник, 10 января 2012 г.

Когда тянет использовать instanseof ...

Строить логику на проверках к какому классу принадлежит обьект - плохо. Аргументы я не обдумывал, но в утверждении интуитивно уверен.

Один из вариантов "хорошего" подхода: Liskov substitution principle. Вкратце - все public методы должны быть еще в самом корне иерархии. Все что выше:
1) должно любым способом их реализовывать;
2) не должно добавлять новых открытых методов и полей.
В чем плюс - если в чьем-то коде есть метод doSomethingWith(SomeBaseClass param) - он  никогда не сломается, какой бы наследник SomeBaseClass ни передавался.

Но иногда надо, чтобы наследники таки отличались от базового класса. Как же тогда использовать эти возможности там, где мы получаем BaseClass?

Я нашел приемлимое решение - все добавляемые методы должны входить в определенные интерфейсы. И проверки делаются:
if (A instanseof CanDoSmthInterface){
((CanDoSmthInterface)A).doSmth();
}

вместо:
if (A instanseof ClassA){
((ClassA)A).doSmth();
}
Это позволяет столь же легко, как и ранее расширять иерархию. Если строго следовать этому принципу, новые методы не повергают архитектуру в хаос - смысл каждого из них отражен в интерфейсе, четко указывающем какая именно абстракция была добавлена к базовому классу.

2 комментария:

  1. Но, тем не менее от использования, instanceof ты так и не ушел. Я однажды сталкивался с ситуацией, когда очень хотелось использовать instanceof: обходил коллекцию, в которой хранились объекты разных типов (но все отнаследованные от одного родителя). Обрабатывать их, естественно, нужно было по разному. Мне тогда подсказали использовать шаблон Посетитель (Visitor). А так, на вскидку, мне не приходят в голову другие ситуации когда очень хотелось бы использовать instanceof.

    ОтветитьУдалить
  2. Раньше изучал шаблон Visitor, но только сейчас, имея пример зачем он может быть нужен, все стало ясно. Он хорошо решит проблемму если нужно обойти иерархию и сделать что-то с каждым объектом (что-то разное, но всегда - единственное действие). В моем случае поведение другое: есть десяток операций над иерархией, в большинстве случаев берется только один Node (get из HahsMap по id), который должен быть определенного типа и выполняются специфичные для него действия.
    Вся структура выглядит так:
    interfaces:
    Container {
    public void addChild(Node node);
    public int getId();
    public Map getChildren();
    public void initialize(String nextLine);
    }

    Replicateable {
    public JSONObject replicate(Node whereToPlace, String replicaName) throws IOException;
    public void registerReplica(Node replica);
    }

    PostProcessable {
    public void postProcess();
    }

    И 4 наследника Node:
    ContextNode implements Container;
    ItemNode implements Replicateable, Container;
    LinkNode implements Replicateable, PostProcessable;
    FieldNode.

    Теоретически это можно сделать с помощью Visitor, но я получу то, чего хотел избежать - в каждом классе будут все 7 методов, половина из которых - пустая.

    ОтветитьУдалить