2012-05-21 5 views
1

Является ли хорошей практикой программирования OO для создания метода в базовом классе, который знал бы о производном классе?Должен ли базовый класс содержать метод, который ссылается на производный класс?

Например, рассмотрим классы A, B, C, D, где класс B наследует от A, а классы C и D наследуют от B. Рассмотрим теперь метод, который является общим для всех классов, которые наследуются от A (включая сам A) и который должен знать о классе B для работы (например: относительное расстояние объектов класса A/B/C/или D от объекта B). Поскольку этот метод является общим для всех классов (A, B, C, D), он звучит логично, что он должен быть помещен в класс A. Однако в этом случае у нас будет метод в базовом классе, который «знал» о производном класс.

Пример:

Class A { 
# simple coordinate system 
    distance_to_complex_coordinate_system (B complexCoord) { 
     return complexCoord.distance_to_simple_coord_system(self) 
    } 

} 

Class B extends A { 
# complex coordinate system 
    distance_to_simple_coordinate_system (simpleCoord) { 
     # do calculations 
    } 

} 

Class C extends A {} 
Class D extends A {} 
... 

В приведенном выше примере distance_to_complex_coordinate_system представляет собой метод, который все простые системы (а именно: A, B, C, и D) координат должны иметь и выполнение точно так же для всех из них , Это не абстрактный класс, и поэтому его нельзя переопределять.

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

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

+0

вы говорите, что «реализация точно так же для всех них», которая заставляет меня верить, что 'A' знает все, что необходимо знать для того, чтобы вычислить значение для' distance_to_simple_coord_system() '. Вы изначально заявили, что «A» должны знать о реализации в «B», но это не похоже на случай после вашего разъяснения. Я подозреваю, что ваши подклассы будут иметь несколько разные реализации 'distance_to_simple_coordinate_system()' иначе зачем переопределять этот метод? – Brad

+0

'distance_to_simple_coord_system' - это метод класса B, и только этот класс знает, как его вычислить. Класс A и все другие классы должны знать, что класс B существует, и поэтому используйте 'distance_to_complex_coordinate_system' – mns

+0

Я не хочу переопределять' distance_to_simple_coordinate_system', этот метод существует только в классе B и нигде больше.Проблема заключается в 'distance_to_complex_coordinate_system', который должен знать, что класс B существует. – mns

ответ

1

Альтернативного решение, основанную на ваших последние разъяснения вашего вопроса.

Я думаю, что функциональность класса B должна быть разбита на вспомогательный класс (состав). Вы указали, что B является неотъемлемой частью знания того, как вычислять distance_to_simple_coord_system(). Можете ли вы удалить B из иерархии классов?

Что-то вроде этого, где A принадлежит экземпляр B, но это уже не подкласс. То, как вы на самом деле создаете и устанавливаете экземпляр B, зависит от вас.

class A { 
    B b = new B(); 

    distance_to_complex_coordinate_system() { 
     // i'm guessing that passing "this" maybe of use to you 
     b.distance_to_simple_coord_system(this); 
    } 
} 

class B { // does not extend A 
    Public distance_to_simple_coord_system(A a) {} 
} 
class C extends A {} 
class D extends A {} 

[EDIT]

После ваших комментариев ...

Я согласен с вами. Что не является чувство правого вот зависимость от класса B от от вашей иерархии и ее не родительского класса. Это принципиально неверно для дизайна OO, и вам нужно удалить общую функцию из этого класса, либо переместив его на A, либо в другой класс вспомогательных помощников.

Примечание: Иногда дизайн ОО не может хорошо служить конкретной иерархии наследования в реальном мире, в отличие от того, что книги из 10 лет назад заставили нас поверить.

Это решение позволяет держать A, B, C, D иерархии для битов, которые необходимы, и я использую интерфейс вспыхнуть общую особенность от B.

class A { 
    Calc calc = new SimpleCalc(); 

    distance_to_complex_coordinate_system() { 
     getCalc().distance_to_simple_coord_system(this); 
    } 

    Calc getCalc() { 
     return this.calc; 
    } 
} 

class B extends A {} 
class C extends A {} 
class D extends A {} 

interface Calc { 
    distance_to_simple_coord_system(A a); 
} 

class SimpleCalc implements Calc { 
    distance_to_simple_coord_system(A a) { 
     // implementation assuming you don't need an "instanceof B" passed in 
    } 
} 

В: Зачем использовать интерфейс?

A: Так что вы можете определить реализацию во время выполнения, как сделать расчет на основе класса A, B, C или D

Считают, что C может потребоваться другой способ расчета его distance_to_complex_coordinate_system, вы можете сделать это ...

/* 
    * Calculator specific to C 
    */ 
    class CalcForC implements Calc { 
     distance_to_simple_coord_system(C c) { 
      // implementation specific to "C" 
     } 
    } 

    class C extends A { 
     Calc calcForC = new Calc(); 

     /* override */ 
     Calc getCalc() { 
      return this.calcForC; 
     } 
    } 

    C c = new C(); 
    c.distance_to_complex_coordinate_system();  // defined on A but calls getCalc() on C 
+0

Я очень ценю вашу помощь. Да, я мог удалить B из иерархии, но тогда мне пришлось бы повторно реализовать в B общую функциональность между A и B. Также B также должен был бы называть 'distance_to_complex_coordinate_system'. Однако я полагаю, что, возможно, я мог бы создать еще один класс AB, который был бы родителем A и B (B не расширяет A), содержащим общую функциональность между ними. Затем отдельно для A и B я мог бы реализовать 'distance_to_complex_coordinate_system' в каждом из них. – mns

+0

У меня все еще есть ощущение, что это как-то искусственное/техническое решение, а не достаточно элегантное. Это концептуально меня озаряет, что мне нужно определить 'distance_to_complex_coordinate_system' в двух местах (как в A, так и в B). – mns

+0

Я думаю, что дизайн после вашего редактирования находится в правильном направлении. Я попытаюсь реализовать его и посмотреть, как он работает. Спасибо за помощь. – mns

0

Если это метод также в правильном контексте A, однако он не может иметь реализацию в A, вы можете объявить его как метод abstract, который должен быть реализован любым производным классом. (Или вы можете иметь реализацию, которая знает только об A, но вы можете override в производных классах.) Если у вас есть ощущение, что вам нужно много перекрестных знаний, вы, возможно, недостаточно абстрагировались.

1

Вы правы, его запахи плохо имеют метод в A, который должен знать о реализации в подклассе. Возможно, вам нужно включить эту функцию в другой класс (а не в иерархию наследования) и одобрить композицию над наследованием в этом случае.

Немного сложно сказать наверняка, что делать, не зная больше о вашем коде. @Styxxy поднимает хороший смысл об использовании абстрактных методов как способ этого, но все зависит от того, что вы имеете в виду, когда говорите «A нужно знать о B».

Если объект создается как A, то как его можно сбрасывать до B, чтобы получить реализацию, о которой вы говорите? Это невозможно.

Однако, если объект создаются как B может вызвать общий метод A, что в свою очередь вызывает абстрактные методы, которые будут переопределены его подклассов (например, B). Это совсем не то, что делает шаблон дизайна Template. Родительский класс A определяет некоторые шаги, которые должны быть приняты, и , а также порядок выполнения этих подклассов.

abstract class A { 
    public void doStuff() { 
     doThis(); 
     doThat(); 
    } 

    abstract void doThis(); 

    abstract void doThat(); 
} 

class B extends A { 
    @Override 
    void doThis() { 
     // implementation 
    } 

    @Override 
    void doThat() { 
     // implementation 
    } 
} 


// from somewhere in code...your subclass B knows doThis() will be called before doThat() 
B b = new B(); 
b.doStuff(); 

Пожалуйста, простите синтаксические ошибки я не валидирующую правильности здесь просто пытаюсь получить через точку

+0

Брэд и @Styxxy благодарю за ваши быстрые ответы. Думаю, я понял ваши предложения (если не поправьте меня) и полностью согласен с вами. Однако моя главная проблема заключается в том, что метод не является абстрактным, это всего лишь метод, который всегда один и тот же, и просто нужно знать о существовании класса B. Я отредактировал вопрос, чтобы сделать этот пункт более чистым. – mns