PDA

Просмотр полной версии : Наследование реализации


SamFR
26.11.2009, 01:15
Здравствуйте.

Давно уже мучаюсь следующим вопросом. AS3 поддерживает только наследование типа, между тем в некоторых случаях очень хорошо было бы применить наследование реализации. В этом, в принципе, и состоит вопрос :)

Возмём следующий пример. Пусть имеется базовый класс ItemList, в интерфейс которого входят методы addItem() и deleteItem(). Это – визуальный класс списка, основанный на работе с единичными элементами. Также имеется производный от ItemList класс ListView, который определяет интерфейсный метод setDataProvider(). Этот производный список рассчитан на работу с поставщиком данных, однако в своей внутренней логике использует унаследованные методы addItem() и deleteItem().

Проблема состоит в том, что оставлять эти методы открытыми нехорошо по той причине, что производный класс уже не рассчитан на их прямое использование. В C++ я применил бы закрытое либо защищённое наследование (т.е. наследование реализации), или же композицию.

Композиция – вещь замечательная, более того, она применима и в AS. Но её не всегда удобно использовать. Например в случае того же ListView придётся добавлять используемый ItemList в качестве ребёнка.

Второе решение – определить базовый класс (назовём его CoreItemList), в котором будет содержаться реализация всех интерфейсных, по сути, методов, но они будут объявлены как защищённые. А от него уже наследовать ItemList, объявив в нём публичные обёртки для интерфейсных методов, а также ItemView, используя унаследованные методы в качестве защищённых. Несмотря на свою простоту, это решение кажется мне немного кривоватым :)

Был бы очень рад услышать ваши мысли, касающиеся данного вопроса)

wvxvw
26.11.2009, 01:23
Это называется over-engineered :) Ну, смотрите, если другой програмист возьмет и переопределит ваш метод и не реализует его как нужно - то кто ему виноват, а зачем было лезть, если не знаешь куда? :)
Ну или если совсем не хотите чтобы метод переопределяли - есть final.
ИМО в виду того, что АС медленный делать всякие "обертки" и остальные красивости - чревато тормозами, и просто того не стоит.

SamFR
26.11.2009, 01:37
Естественно, в некорректной реализации переопределённого метода будт виноваты только кривые руки программиста :) Нет, я не про переопределение.

Я про то, чтобы ограничить унаследованный интерфейс в случае, если производный класс не рассчитан на его прямое использование. Например, вызов ListView.removeItem() приведёт к тому, что будет удалён элемент списка, несмотря на то, что соответствующий элемент данных присутствует в поставщике. Это может привести к последующим ошибкам внутренней логики списка (например, когда от поставщика придёт сообщение об изменении сответствующего элемента данных).

ИМХО в виду того, что АС медленный делать всякие "обертки" и остальные красивости - чревато тормозами, и просто того не стоит.
Вот и я ж о том же)

etc
26.11.2009, 07:47
public override function addItem(...):void {
throw new IllegalOperationError();
}

public override function removeItem(...):void {
throw new IllegalOperationError();
}

...

private function update(...):void {
...
super.addItem(...);
...
super.removeItem(...);
...
}

Единственное что методы в родительском классе нужно делать вроде этого:

public function addItem(...):void {
this._addItem(...);
}


Т. е. реализация метода внутри _addItem и все вызовы в пределах класса идут через this._addItem, а не this.addItem.

switcher!
26.11.2009, 12:30
Единственное что методы в родительском классе нужно делать вроде этого

public function addItem(...):void {
this._addItem(...);
}

можно ли подробнее узнать зачем нужен метод-посредник? Для гибкости кода?
Ничего более путного, чем изменение количества аргументов в голову не приходит. А-ля:
public function addItem(myVar:int):void {
this._addItem(myVar);
//или this._addItem(myVar, 1.5);
}

private function _addItem(myVar:int, anotherVar:Number = NaN):void {
...
}
опыта мало. увы...

+ ко всему прочему, это ситуация напомнила мне вызовы вроде:
dispatchEvent() -> dispatchEventFunction() -> метод-обработчик
с малопонятным вызовом метода-посредника. Хотя здесь можно списать на нативную реализацию, скрытую за 7-ю печатями.

etc
26.11.2009, 14:30
можно ли подробнее узнать зачем нужен метод-посредник?

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

switcher!
26.11.2009, 14:53
да, точно. Кстати, очень своевременная информация. Спасибо!

SamFR
26.11.2009, 16:36
etc, так я всегда, в принципе, и делал. А сейчас что-то вдруг захотелось узнать, нет ли более красивого решения :)
Мне в данном случае не очень нравится вызов дополнительного метода (хотя такое решение часто достаточно гармонично вписывается в логику класса). Но ещё хуже – набор интерфейсных методов, вызывающих безусловные ошибки. В документацию не все заглядывают, а автокомплит не дремлет :) Да и просто как-то это...

r_r_f_r
26.11.2009, 16:48
[Deprecated(message="Поскольку-постольку", replacement="сюда-то", since="v 3.42.3.12.123.4.123.123")]

SamFR
26.11.2009, 17:02
Ммм... Интересно, нужно будет попробовать. Но ведь он же только из доков и автокомплита исключает, правильно я понял? Хотя это уже лучше)

Добавлено через 3 минуты
"v 3.42.3.12.123.4.123.123"
Это с учётом количества изменённых печатных символов? Всегда хотел такую SVN :)

r_r_f_r
26.11.2009, 17:18
В переопределённом методе выбрасываем эсепшен, получили проверку на этапе выполнения, а на этапе компиляции-написания мы можем только предупредить разработчика этим тэгом.

И из автокомпилита не исчезает, просто варнинг(на флексовом компиляторе).

etc
26.11.2009, 18:55
Можно и [Exclude] повесить. Только я не помню, работает он на методы или нет.

SamFR
26.11.2009, 19:12
Спасибо за советы, буду пробовать.

Добавлено через 2 часа 25 минут
Вроде бы [Exclude] должен работать для свойств, методов, событий, стилей и эффектов:
[Exclude(name="elementName", kind="property|method|event|style|effect")]
Жаль, конечно, что он не запрещает обращаться к исключённому члену, но иначе это было бы уже не наследование типа =)