Найти декоратор определенного типа при использовании шаблона декоратора? - PullRequest
0 голосов
/ 29 апреля 2018

Я создаю приложение, которое должно использовать несколько типов похожих датчиков. Поскольку датчики также могут иметь различное поведение или комбинации поведения, я решил использовать шаблон декоратор .

Короче говоря, у меня есть иерархия, которая выглядит следующим образом:

enter image description here

Таким образом, любой из конкретных классов ISensorDecorator может украсить (обернуть) любой из конкретных классов IMeasureSensor, но поскольку бетон ISensorDecorator также является бетоном IMeasureSensor, они могут обернуть друг друга. Так например

IMeasureSensor sensor = new FilteredSensorDecorator(
    new CalibratedSensorDecorator(
        new AccelerometerSensor()
    )
);

является действительным утверждением, которое объявляет отфильтрованный и откалиброванный датчик акселерометра.

Теперь допустим, у меня есть метод с именем setCalibration() в CalibratedSensorDecorator. Очевидно, я не могу позвонить

sensor.setCalibration();

потому что IMeasureSensor не имеет setCalibration() метода. И пытается использовать

((CalibratedSensorDecorator)sensor).setCalibration()

также не будет работать, поскольку sensor - это FilteredSensorDecorator.

Как я могу добраться до CalibratedSensorDecorator в этом конкретном случае, и в более общем смысле - до любого конкретного декоратора в любой «цепочке» декораторов? Я не хочу хранить их как отдельные переменные, я хочу делать это динамически.

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Хотя в ответе Сагар обсуждаются некоторые (действительные) причины для рассмотрения вопроса о том, чтобы использовать другой подход, а не шаблон декоратора, я нашел рабочее решение для реальной проблемы поиска правильного декоратора.

/**
 * Walks the decorator hierarchy recursively from the outside in (excluding the final
 * IMeasureSensor which is not a ISensorDecorator), and returns the decorator of the given class.
 * If none can be found, null is returned.
 */
IMeasureSensor findDecorator(IMeasureSensor sensor, Class decoratorClass){
    if( ISensorDecorator.class.isAssignableFrom(sensor.getClass()) ){
        return (sensor.getClass() == decoratorClass) 
            ? sensor 
            : findDecorator(((ISensorDecorator) sensor).getDecoratee(), decoratorClass);
    } 
    else
        return null;
}

Метод ISensorDecorator.getDecoratee() просто возвращает «decoratee», то есть IMeasureSensor, которое декоратор украшает.

public IMeasureSensor getDecoratee(){
    return mMeasureSensor;
}

Затем вы можете использовать findDecorator(), чтобы найти (самый внешний) декоратор заданного типа, например так:

IMeasureSensor sensor;

...

CalibratedSensorDecorator s = (CalibratedSensorDecorator) findDecorator(sensor, CalibratedSensorDecorator.class);
0 голосов
/ 29 апреля 2018

Поскольку это вопрос дизайна, правильного ответа не будет, вам нужно сделать выбор, который может быть хорошим или не очень хорошим.

Не следует добавлять метод для определенного класса, поскольку он будет нарушать Liskov substitution principle

Объекты в программе должны заменяться экземплярами их подтипов без изменения правильности этой программы.

Вы можете инициализировать calibration в конструкторе CalibratedSensorDecorator и использовать его при выполнении требуемой функции.

Если это не соответствует вашим требованиям, то, возможно, CalibratedSensorDecorator не входит в вашу иерархию датчиков. Попробуйте разделить его и использовать шаблон «Стратегия», чтобы решить, какой из них использовать.

Редактировать 1:

что я понимаю, это не говорит о том, что вы не должны добавлять методы к подтипам?

Да, вы правы. Он не запрещает добавлять методы, но если методы изменяют состояние объекта, его следует пересмотреть. Все эти шаблоны являются лишь рекомендациями, которые можно настроить в соответствии с нашими потребностями.

Чтобы объяснить мое обоснование:

Представьте, что вы создали setCalibration() на CalibratedSensorDecorator. Вы можете использовать CalibratedSensorDecorator как для внутреннего разработчика, так и для внешнего разработчика. Вы создали Фабрику, которая просто возвращает IMeasureSensor следующим образом:

public IMeasureSensor getCalibratedSensor(){
    ...
}

Теперь пользователь вашего API просто получает это и рад, что его / ее текущий код работает. Но понимает, что он / она пропустил до setCalibration(), который был найден после часов отладки. Более того, он / она должен написать код проверки типа и кода приведения типов, чтобы использовать эту функцию, что может не подойти для чистого кода.

Вы должны стараться, чтобы ваши классы были как можно более неизменными, чтобы отладка и обслуживание были легкими. Нет ничего плохого в воссоздании объекта, так как старый будет собирать мусор.

Опять же, это только мое предложение, ваше решение тщательно обдумать, что лучше для вашего варианта использования. Вы по-прежнему можете использовать новый подход к созданию метода, если он является обязательным, и обеспечить надлежащую документацию, чтобы пользователь мог понять его использование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...