LINUX.ORG.RU

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

 ,


0

1

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

Вот тут есть простой пример: http://info.javarush.ru/translation/2014/06/26/Наследование-против-композиции...

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

Однако, мы можем задаться вопросом, а не являются ли эти проблемы, проблемами специфичными для его среды? Не рановато ли нам обобщать данные утверждения?


Insect := Object clone do(
  move := method("move" println)
  attack := method(move; "attack" println)
)

Bee := Insect clone do(
  move := method("fly" println)
  attack := method(resend)// этот метод просто для эквивалентности, его можно было бы вообще убрать, это глупость.
)

bee := Bee clone

bee attack

#>>>> fly
#>>>> attack

Упс. Для Io данное утверждение не справедливо, тут все работает. Следовательно что? А то что тезис «Композиция лучше наследования» должен звучать как для группы языков «Композиция лучше наследования для некоторой группы языков, а для другой группы — наоборот». Не следует употреблять такие общие понятия как «ООП», «Проектирование», когда речь идет лишь о частных случаях.

Вот и человек, несколькими тредами ниже, слегка запутался, и *думает*, что он плодит овермиллион классов «в угоду проектирования», тогда как он это делает по причине среды программирования.

Не стоит слишком увлекаться обобщениями, друзья.



Последнее исправление: filequest (всего исправлений: 3)

Поздравляю, ты опять не понимаешь русского языка. Там сказано, почему нельзя опускать super.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Но в моем случае super не нужен. Ты как раз повторяешь ту же ошибку, что то там про опускание супер относится к *той* реализации а не к этой.

filequest
() автор топика
Ответ на: комментарий от filequest

Однако код является дубликатом суперкласса. ( метод attack() делает более сложные вещи, чем просто вывод строки). Это не соответствует правильному конструированию программного обеспечения, повторяющегося кода не должно быть.

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

/thread

x3al ★★★★★
()
Ответ на: комментарий от filequest

Что именно мне читать внимательно? Что ты предлагаешь скопипастить весь код переопределяемого метода в каждого потомка, заменив нужные вещи, и назвать это «композиция не нужна»?

x3al ★★★★★
()

это выглядит немного нелепо, у нас как бы, пчела уже не насекомое

Наследование существует не для таксономии.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Я ничего не предлагаю, то что ты процитировал, относится к тому, что ему пришлось дублировать в подклассе и move и attack, в моем коде этого нет.

filequest
() автор топика
Ответ на: комментарий от x3al

Наследование существует не для таксономии.

Иерархия должна быть логичной, иначе это не код, а дерьмо.

filequest
() автор топика
Ответ на: комментарий от filequest

Ты же троллишь.

Устранить метод attack() подкласса. Это сделает подкласс зависимым от реализации метода attack() суперкласса. Если attack() метод в суперклассе позже изменяется (что вне вашего контроля), например, метод attack() суперкласса начнет использовать другой метод для перемещения, подкласс нужно будет тоже изменить. Это плохо инкапсуляции.

Вот именно это ты и сделал.

x3al ★★★★★
()
Ответ на: комментарий от x3al

И да, «вне вашего контроля» означает, что над кодом может работать больше одного человека. Я понимаю, что тебе это сложно представить, но постарайся.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Даже так? Ого. Это реально беспробудная тупость тогда, значит, да, я неправильно понял. То есть, обобщать поведение с точки зрения «этого ООП» тоже плохо? Вот это они долбят.

Ну тогда подправим. Смотри исправленную версию через минуту. Только смысл то в этом какой? Он же там в методе дочернего класса все равно перенаправляет суперклассу

filequest
() автор топика
Ответ на: комментарий от x3al

И да, «вне вашего контроля» означает, что над кодом может работать больше одного человека. Я понимаю, что тебе это сложно представить, но постарайся.

то есть, по этой логике получается, что суперкласс может менять кому не лень, а подкласс нельзя? Чем младше тем важней?

filequest
() автор топика
Ответ на: комментарий от x3al

В любом случае, если суперкласс изменился, можно этот метод просто дописать, так что это глупость.

filequest
() автор топика

Вообще должно быть как-то так:

IInsect protocol = 
  move! -> Unit
  attack! -> Unit

Bee implement IInsect =
  move! = println! "Fly"
  attack! = println! "Attcack

Все инсекты всё делают иначе, лучше сделать его протоколом/интерфейсом, а пчёлка уже будет классом или объектом.

holuiitipun
()

Фееричное чудо вызывает в базовом классе свой метод, спроектированный под оверрайд. Чего там рассматривать? Какие оттуда могут быть выводы?

Касательно IO, у него, судя по твоему коду, все методы вирутальные. В рассматриваемом упоротом случае это в плюс — не надо городить интерфейсов (а в яве похоже виртуалов нет, вот чел и выкручивается, так что Да). Но когда начнешь проектировать конструкторы-инициализаторы, начнутся головняки. Та же фигня в объектив-сишке, они даже ввели понятие designated initializer и тупо пишут его в документации/комментариях к объявлениям. Если случайно вызвать не его, то произойдет какая-нибудь неуловимая шляпа а-ля UB и будешь долго ковыряться отлаживать.

Лучше таки явно указывать, что будет виртуальным, а что не будет, хотя подход конкретно в крестах мне не нравится — вызов виртуала выглядит абсолютно так же, как вызов обычного метода. Но они любят такое, когда какой-нибудь безобидный x += 1 неожиданно для всех разворачивается в 100500 тактов и сетевой запрос, поэтому пусть.

arturpub ★★
()
Ответ на: комментарий от arturpub

Но когда начнешь проектировать конструкторы-инициализаторы, начнутся головняки. Та же фигня в объектив-сишке, они даже ввели понятие designated initializer и тупо пишут его в документации/комментариях к объявлениям. Если случайно вызвать не его, то произойдет какая-нибудь неуловимая шляпа а-ля UB и будешь долго ковыряться отлаживать.

не знаю, как в objc, но в Io инициализатор вызывается вместе с созданием нового объекта. Когда вызывается clone, интерпретатор ищет init, просто метод с таким названием в предках. И если находит, он его вызывает, и происходит инициализация вновь созданного объекта или класса, все прозрачно, никаких проблем нет. Если надо создать без инициализации, надо вызывать cloneWithowtInit. Если надо проверить, есть ли он, и что из себя представляет, его можно посмотреть в репле. Не представляю, какие там проблемы могут быть.

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

filequest
() автор топика
Ответ на: комментарий от arturpub

вызов виртуала выглядит абсолютно так же, как вызов обычного метода

Я хоть кресты и не люблю, но это правильно. На самом деле, в Io не может быть никаких виртуалов, так как там все «виртуально», а так как ничего другого нет, то и смысла что то обозначать и разделять как виртуальный/невиртуальный тоже нет. Но, на самом деле, объект это не просто структура, это абстракция. Соответственно, нас не должно интересовать, как этот объект отвечает на сообщение, где он ищет какое значение, берет ли он его из предка, или имеет свое. Это абсолютно верно с точки зрения ООП. Нас интересует только то, что объект отвечает на сообщение.

filequest
() автор топика
Ответ на: комментарий от filequest

Субкласс может принимать доп.аргументы в конструктор (иначе придется часть конструктора выносить как доп.метод, а это тот же хер, только в другой руке). Например NSWindowController инициализировать можно несколькими convenience-способами (кода внутри таких хелперов может быть немало), но выделенный конструктор, который по соглашению вызывается следующими субклассами как [super init...], только один, ну или два-три в запущенных случаях.

Касательно проблем — если в невыделенных конструкторах нужен common snippet, то его нельзя сделать методом с популярным именем, иначе ты при [self _commonInit] воткнешься в вызов чужого уровня, если он тоже думал что он самый умный и сам реализовал -[_commonInit]. В принципе это и с конструкторами может произойти, но вызывать один хелпер из другого это выстрел в ногу, и все это знают. Приходится выносить общие блоки в обычные сишкины функции, которые очевидно вызываются именно на том уровне, где были реализованы. В общем-то это и есть то деление на виртуал/невиртуал, и с нормально различимым синтаксисом. Это ни хорошо, ни плохо, но как видишь дефолтная виртуальность тут может подколоть.

arturpub ★★
()
Ответ на: комментарий от filequest

Я хоть кресты и не люблю, но это правильно. На самом деле, в Io не может быть никаких виртуалов, так как там все «виртуально», а так как ничего другого нет, то и смысла что то обозначать и разделять как виртуальный/невиртуальный тоже нет.

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

arturpub ★★
()

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

потому, что т.н. «среда» — это часть архитектуры програмного решения, очевидно же...

и эти ваши ООП-java-проблемы можно описать одной фразой: если у профессионала основной инструмент молоток, то все проблемы ему кажутся гвоздями.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.