Как да извикам заместени методи в подклас? Потенциален кандидат за рефакторинг

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

Така че накарах всеки клас да наследи SuperClass и неговия метод doSomething() и имах SubClassSpecial1 и SubClassSpecial2 да заменят със собствен метод doSomeThing.

Всичко беше наред, докато не написах метод, който изглеждаше нещо подобно

void fooBar(SuperClass obj) {
    obj.doSomething()
}

и може да се нарече fooBar( new SubClassSpecial1() );

Проблемът е, че класът по време на изпълнение на променливата obj сега е този на нейния суперклас и по този начин ще извиква методите, дефинирани в суперкласа. Бих могъл, разбира се, да направя абстрактен метод doSometing() в суперкласа и да накарам всеки подклас да реализира своя собствена версия, но това би дублирало кода в три от класовете. И искам да избегна това...

Бих загубил всяка печалба, която полиморфизмът дава, ако имах много разклонения

if(obj.getClass().getName() == "SubClassSpecial1" )  ((SubClassSpecial1) obj).doSomething()K;
else if ...

И така, какво трябва да направя, за да направя дизайна по-елегантен и нехакерски?


person oligofren    schedule 02.11.2009    source източник
comment
Тази статия трябва поне да бъде маркирана с езика за програмиране, с който кодирате.   -  person Percutio    schedule 02.11.2009
comment
фиксирани. добави java таг. Благодаря. но си мислех, че това е общ проблем с дизайна, който надхвърля езиковите бариери.   -  person oligofren    schedule 02.11.2009
comment
Можете ли да ни дадете фрагменти от дефинициите на вашия клас?   -  person SMART_n    schedule 02.11.2009
comment
Проблемът изглежда беше, че не бях разбрал как работи наследяването на Java. Мислех, че ако имам нещо като class SuperClass { doIt(){print(В суперкласа); } } class SubClass extends SuperClass { doIt(){ print(В подкласа); } } и имаше фрагмент от код като този SubClass o = new SubClass(); ((Суперклас) o).doIt(); ще се отпечата в суперкласа. Оказа се, че не е и моите предположения бяха грешни и полиморфизмът всъщност работи в моя полза.   -  person oligofren    schedule 02.11.2009


Отговори (3)


Това, което описахте, трябва да работи добре.

Всъщност obj.doSomething() извиква ли внедряването на суперкласа? Ако е така, не го заменяте правилно. Проверете дали не сте променили подписа във вашите заменени версии.

person Terry Wilcox    schedule 02.11.2009
comment
Е, не трябва ли да прави това? Типът по време на изпълнение на обекта е деклариран като този на неговия суперклас и по този начин той трябва да извика версията на суперкласа. Ако принудя ((SubClassSpecial1) obj).doSomething() ще извика версията на подклас. Или моите предположения тук са грешни за това как работят класовете за наследяване и изпълнение? - person oligofren; 02.11.2009
comment
Хм ... Написах тестова програма, която показва недостатъци в моите предположения за това как работят промените в типа Runtime на обект. Трябва да проверя това, преди да се върна... - person oligofren; 02.11.2009
comment
Вашето предположение е грешно. Не е необходимо да предавате obj към SubClassSpecial1, за да получите метода SubClassSpecial1 doSomething(), това ще се случи автоматично. Това е полиморфизъм. Опитайте някои тестове. - person Terry Wilcox; 02.11.2009
comment
Ето как работи Java. Единственото изключение от това е по време на конструиране, ако конструктор на супер клас извика операция, тогава операцията върху супер класа се използва, тъй като извлеченият екземпляр все още не е конструиран и неговите полета биха били невалидни. Ако трябва да направите това, тогава ще трябва да добавите операция за стартиране, която да се изпълни след изграждането. - person vickirk; 02.11.2009
comment
Направих го и ти наистина си прав. Намерих и фин бъг, докато го правех. Благодаря! - person oligofren; 02.11.2009
comment
@vickirk: Дори в конструктор се извиква методът на подкласа. Но проблемът, който описвате (т.е. подкласът все още не е напълно конструиран) е точната причина, поради която трябва да извиквате САМО private или final методи в конструктор. - person janko; 04.11.2009

Малко съм объркан от тази част от въпроса ви:

Разбира се, това, което исках да направя, беше всеки обект да извика своя собствена версия на doSomething(), но не успях да осъзная, че за да направя това, obj трябва да бъде деклариран като един от методите на подклас. И сега е бъркотия.

Разбира се, декларацията няма значение, методът doSomething() винаги ще бъде извикан според типа на времето за изпълнение на класа.

Така че мисля, че това, което се опитвате да направите, трябва да работи добре, напр. всички тези декларации могат да се използват за преминаване към метода foobar:

SuperClass sc1 = new SubClassSpecial1();
SubClassSpecial2 sc2 = new SubClassSpecial2();
//etc..
person NickDK    schedule 02.11.2009
comment
Добре, ще се опитам да редактирам тази част и да се перифразирам. Това, което имах предвид, беше, че исках обектът да извика свой собствен (ако е заменен) метод, а не този от неговия суперклас. И проблемът е, че тъй като е деклариран (в декларацията на метода) като екземпляр на суперкласа, неговият тип клас по време на изпълнение ще бъде този на неговия суперклас и по този начин ще извика метода на суперкласа. Дано това има някакъв смисъл... - person oligofren; 02.11.2009
comment
Не, вашето предположение е малко по-различно, класът, който използвате, когато създавате нов обект с ключовата дума „new“, определя типа време на изпълнение на вашия обект. Така че в моя пример променливата SuperClass sc1 съдържа препратка към обект, чийто тип на изпълнение е SubClassSpecial1. Сега това може да е малко объркващо, надявам се да го разберете :-) - person NickDK; 02.11.2009
comment
Благодаря, започнах да подозирам, че нещо не е наред след отговора на Тери и разбрах, че Java не прави това, което смятах, че е интуитивният начин да го направя, като напиша тестов код. Все още намирам това поведение за малко странно, но поне сега знам :) - person oligofren; 02.11.2009

Когато имате това:

void fooBar(SuperClass obj) {
    obj.doSomething();
}

тогава типът време на компилиране на obj е SuperClass. Това означава, че компилаторът ще провери дали SuperClass има doSomething() метод.
По време на изпълнение можете да замените подклас на SuperClass, това е Принцип на заместване на Лисков. Метод foobar() не знае и не трябва да знае какъв е типът време на изпълнение на obj, само че той произлиза от SuperClass и така doSomething() може да бъде извикан.

Що се отнася до вашия пример:

fooBar( new SubClassSpecial1() );

В този случай случайно знаете, че типът време на изпълнение на параметъра е SubClassSpecial1, което конкретно замества doSomething(). Във всички случаи се извиква правилният метод.

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

person quamrana    schedule 03.11.2009
comment
Благодаря ти, quamrama! За мен това беше най-информативният от отговорите, въпреки че на основното ми оплакване вече беше отговорено от някой друг. Накарахте ме да разбера много повече за това как работи това и съветът за рефакторинг всъщност е полезен. Страхотен отговор! - person oligofren; 04.11.2009