може ли замяната на метод да бъде предотвратена чрез низходящо предаване към суперклас?

Опитвам се да разбера дали отговорът на следния въпрос е един и същ на всички основни ООП езици; и ако не, тогава как се различават тези езици.

Да предположим, че имам клас A, който дефинира методи act и jump; метод act извиква метод jump. Подкласът B на A отменя метод jump (т.е. използва се подходящият синтаксис, за да се гарантира, че когато се извиква jump, се използва имплементацията в клас B).

Имам обект b от клас B. Искам да се държи точно така, сякаш е от клас A. С други думи, искам jump да се изпълни с помощта на имплементацията в A. Какви са моите възможности на различни езици?

Например, мога ли да постигна това с някаква форма на понижаване? Или може би чрез създаване на прокси обект, който знае кои методи да извика?

Бих искал да избегна създаването на съвсем нов обект от клас A и внимателното настройване на споделянето на вътрешно състояние между a и b, защото това очевидно не е устойчиво на бъдещето и е сложно. Също така бих искал да избегна копирането на състоянието на b в чисто нов обект от клас A, защото може да има много данни за копиране.

АКТУАЛИЗИРАНЕ

Зададох този въпрос конкретно за Python, но изглежда, че това е невъзможно да се постигне в Python и технически може да се направи... донякъде..

Изглежда, че освен техническата осъществимост, има силен аргумент срещу това от гледна точка на дизайна. Питам за това в отделен въпрос.


person max    schedule 16.01.2012    source източник
comment
Наистина – в Python концепцията за кастинг дори не съществува. Възможно е да се хакне голяма част от интроспекцията, за да работи - но предполагам, че в други динамични езици това също не би трябвало да е възможно.   -  person jsbueno    schedule 17.01.2012
comment
Делегирайте имплементациите на act и преминете към някой друг клас (може би стратегия), след което не се занимавайте с B. Като цяло, ако вашият съществуващ дизайн ви кара да търсите начини да заобиколите добрия OO дизайн, време е да направите крачка назад и да попитате защо.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox: но не може ли този проблем да възникне потенциално при всяко наследяване с динамично обвързване? В такъв случай аз (потенциално) трябва да съм готов да преработя всеки код от наследяване към някакъв друг подход, когато възникне този проблем. Това би направило перспективата за използване на полиморфизъм много непривлекателна. какво ми липсва   -  person max    schedule 17.01.2012
comment
Подкласирането е свързано изцяло с промяна на поведението. Ако искате вашият екземпляр b да действа така, сякаш е A, тогава той не трябва да бъде B на първо място. Ако бях във вашата ситуация, щях да приема, че наследяването е лош дизайн и да премина към композиция. например има само A, което използва стратегическия модел за прилагане на различни поведения.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox: така бихте ли казали, че е малко вероятно да се наложи да се потиска полиморфизмът; но ако в моето приложение го виждам като отделна възможност, трябва да опитам вместо това да използвам стратегически модел, който е строго по-мощен, но има малко по-високи разходи за кодиране?   -  person max    schedule 17.01.2012
comment
Вие не потискате полиморфизма, вие потискате наследството. И ако трябва да го потиснете, не трябва да го използвате в началото. Предпочитането на композицията пред наследяването е добра философия на дизайна. Наследяването не винаги е подходящо и опитът да се хакнат основните OO механизми, за да може дизайнът да работи, е огромен предупредителен знак.   -  person Terry Wilcox    schedule 17.01.2012
comment
@TerryWilcox можеш ли да поставиш коментарите си в отговор, моля?   -  person max    schedule 17.01.2012


Отговори (4)


Коментарите повториха: Предпочитайте композицията пред наследяването.

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

Композицията обикновено е по-доброто решение. Делегирането на различно поведение на вашия обект към различен обект (или обекти) може да намали или елиминира нуждата ви от подкласове.

Във вашия случай поведенческите разлики между клас A и клас B могат да бъдат капсулирани в модела на стратегията. След това можете да промените поведението на клас A (и клас B, ако все още се изисква) на ниво инстанция, просто като зададете нова стратегия.

Стратегическият модел може да изисква повече код в краткосрочен план, но е чист и поддържаем. Промяната на методите, маймунските корекции и всички онези готини неща, които ни позволяват да се ровим в нашата специфична езикова реализация, са забавни, но потенциалът за неочаквани странични ефекти е голям и кодът обикновено е труден за поддръжка.

person Terry Wilcox    schedule 17.01.2012

Това, което питате, е напълно несвързано/не се поддържа от ООП програмиране.

Ако подкласирате обект A с клас B и замените неговите методи, когато се създаде конкретно копие на B, тогава всички заменени/нови реализации на базовите методи се свързват с него (или говорим за Java, или C++ с виртуални таблици и т.н.) .

Създали сте обект B.
Защо бихте очаквали, че можете/ще/трябва да можете да извикате метода на суперкласа, ако сте заменили този метод?

Можете да го наречете изрично, разбира се, напр. като извикате super вътре в метода, но не можете да го направите автоматично и кастингът също няма да ви помогне да направите това.

Не мога да си представя защо бихте искали да направите това.
Ако трябва да използвате клас A, използвайте клас A.
Ако трябва да замените функционалността му, използвайте неговия подклас B.

person Cratylus    schedule 16.01.2012
comment
Не мога да ви кажа защо език като C++ поддържа невиртуални методи, но го прави. - person toddsundsted; 17.01.2012
comment
virtual се използва в C++, за да се декларира, че дадена функция може да бъде заменена. След като функцията е декларирана virtual, тогава методът на подкласа се изпълнява по време на изпълнение. Т.е. полиморфизмът не се изпълнява по подразбиране, както в Java. Но това не е свързано с въпроса за ОП - person Cratylus; 17.01.2012
comment
@user384706: да, въпросът ми е дали нещо може (и трябва) да се направи, след като замяната вече е активирана в дефиницията на класа (в случай на C++ се използва ключова дума virtual). - person max; 17.01.2012

Повечето езици за програмиране се затрудняват да поддържат динамично изпращане на виртуални функции (случаят на извикване на заменения метод jump в подклас вместо имплементацията на родителския клас) -- до такава степен, че заобикалянето или избягването му е трудно. Като цяло специализацията/полиморфизмът е желана характеристика - може би цел на ООП на първо място.

Разгледайте статията в Уикипедия за Виртуални функции, която дава полезен преглед на поддръжката за виртуални функционира в много езици за програмиране. Това ще ви даде откъде да започнете, когато обмисляте конкретен език, както и компромисите, които трябва да претеглите, когато разглеждате език, където програмистът може да контролира как се държи изпращането (вижте раздела за C++, например).

Толкова свободно, отговорът на вашия въпрос е: „Не, поведението не е еднакво във всички езици за програмиране.“ Освен това няма независимо от езика решение. C++ може да е най-добрият ви залог, ако имате нужда от поведението.

person toddsundsted    schedule 16.01.2012
comment
Без virtual полиформизмът не работи в C++. Това не е това, което OP пита. Това е същото, тъй като подкласът никога не е дефинирал метода - person Cratylus; 17.01.2012
comment
Звучи точно като OP иска извикване на невиртуален метод (a la C++). - person toddsundsted; 17.01.2012
comment
Пропускате смисъла. Ако методът не е виртуален, това е същото като никога да не бъде отменян. т.е. извиква се само методът от клас А. Никога от клас B. OP иска да извика метод от клас B и понякога, ако иска, да извика и метод от клас A. - person Cratylus; 17.01.2012
comment
Съжалявам за объркването. @user384706: прав си, не искам да променям дефиницията на класа. Дори ако мога да заменя виртуалните с невиртуални методи в клас A, това няма да работи, тъй като тогава много други потребители на клас B, предадени като обект от клас A, ще бъдат силно изненадани, когато клас B започне да се държи като клас A. - person max; 17.01.2012
comment
@max: Значи имате случай на употреба? Това не е теоретичен въпрос? Какво точно се опитвате да направите? - person Cratylus; 17.01.2012
comment
Да, не бих могъл да направя този въпрос по-подробен, без да объркам всички. Затова зададох отделен въпрос тук: stackoverflow.com/questions/8886568/ - person max; 17.01.2012

Всъщност можете да направите това с Python (нещо като), с някои ужасни хакове. Изисква да внедрите нещо подобно на обвивките, които обсъждахме в първия ви специфичен за Python въпрос, но като подклас на B. След това трябва да приложите и прокси за запис (обвиващият обект не трябва да съдържа нито едно състояние, обикновено свързано с йерархията на класа, той трябва да пренасочи целия достъп до атрибути към основния екземпляр на B.

Но вместо да пренасочите търсенето на метод към A и след това да извикате метода с обвития екземпляр, бихте извикали метода, предаващ обекта на обвивката, като self. Това е законно, защото класът на обвивката е подклас на B, така че екземплярът на обвивката е екземпляр на класовете, чиито методи извиквате.

Това би бил много странен код, изискващ да генерирате динамично класове, като използвате едновременно IS-A и HAS-A връзки. Вероятно също така ще се окаже доста крехко и ще има странни резултати в много ъглови случаи (като цяло не можете да напишете 100% перфектни обвиващи класове в Python точно, защото този вид странно нещо е възможно).

Напълно оставям настрана дали това е добра идея или не.

person Ben    schedule 16.01.2012