Re[4]: Зачем нужны иерархии реализаций?
От: Alexey Chen Чили  
Дата: 02.07.05 23:28
Оценка: +1
Здравствуйте, alexeiz, Вы писали:

A>"Alexey Chen" wrote:

>> Сразу вспоминается старый прикол про то что наследование убивает
>> инкапсуляцию.

A>Каким это образом наследование убивает инкапсуляцию? Если у тебя базовый класс инкапсулирует внутренности private доступом, то никаким образом ты не расширишь доступ в наследуемом классе.


class Потомок : private База

Это означает что _потомок_ знает о структуре базы. Инкапсуляция нарушена, поскольку информация о реализации используется другим типом обьектов.

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

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

При наличии же обратной связи ('шаблонный метод'), такое наследование играет чисто имплементационное значение. Просто такой структурный паттерн поддерживается языком и обеспечивает минимум оверхеда на обратную связь, но и увеличивает жёсткость системы.
Re[5]: Зачем нужны иерархии реализаций?
От: alexeiz  
Дата: 03.07.05 01:14
Оценка:
"Alexey Chen" <17658@users.rsdn.ru> wrote in message
news:1253005@news.rsdn.ru
> Здравствуйте, alexeiz, Вы писали:
>
>> "Alexey Chen" wrote:
>>> Сразу вспоминается старый прикол про то что наследование убивает
>>> инкапсуляцию.
>
>> Каким это образом наследование убивает инкапсуляцию? Если у тебя
>> базовый класс инкапсулирует внутренности private доступом, то
>> никаким образом ты не расширишь доступ в наследуемом классе.
>
> class Потомок : private База

Вообще-то, я имел ввиду даже public наследование.

>

> Это означает что _потомок_ знает о структуре базы. Инкапсуляция
> нарушена, поскольку информация о реализации используется другим типом
> обьектов.

Ты имеешь ввиду object layout? В таком случае это просто детали реализации. В принципе, можно придумать такой object model, чтобы object layout для потомка никак не зависел от базы.

Но если мы дело с интерфейсами, то это вопрос совершенного другого типа. В таком случае потомок знает о базе, только то, что база позволяет ему узнать через public и protected интерфейсы. Отличается это от информации доступной всему остальному миру, только protected интерфейсом. В таком случае потомок узнаёт о базе больше именно через protected интерфейс.

> Но если потомок знает о базе не больше чем любой другой класс, то это

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

Реализацией. Мы наследуем от конкретной базы в данном случае, потому что хотим, чтобы она имела определённую реализацию, определённое поведение. В свете отношения is-a мне это представляется логичным.
Posted via RSDN NNTP Server 1.9
Re[6]: Зачем нужны иерархии реализаций?
От: Alexey Chen Чили  
Дата: 03.07.05 01:51
Оценка: +1
Здравствуйте, alexeiz, Вы писали:

>> Это означает что _потомок_ знает о структуре базы. Инкапсуляция

>> нарушена, поскольку информация о реализации используется другим типом
>> обьектов.

A>Ты имеешь ввиду object layout? В таком случае это просто детали реализации. В принципе, можно придумать такой object model, чтобы object layout для потомка никак не зависел от базы.


>> Но если потомок знает о базе не больше чем любой другой класс, то это

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

A>Реализацией. Мы наследуем от конкретной базы в данном случае, потому что хотим, чтобы она имела определённую реализацию, определённое поведение. В свете отношения is-a мне это представляется логичным.


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

Если жёсткой связи между типами нет (по реализации) есть только связь по контракту, то наследование служит лиш реализационным механизмом делегирования и обратного вызова, накладывая при этом невсегда нужную жёсткую связь. Получается что наследнику реализация базы не важна (инкапсуляция не нарушается), но важна клиенту связки наследник-база. И что мешает при необходимости сменить базу? Наследование и мешает, потому как идёт в разрез с реальностью, а именно независимостью этих типов. Когда в предметной области такие связи явно заданы (is-a) то такой задачи как смена базы (в рамках предметной области) не возникнет, но всёравно может возникнуть в процессе рефекторинга и переиспользования кода. Кто-то (вроде Буч) писал, что прогу можно анализировать как по классам, так и по обьектам. И, ИМХО, наследование неразрушающее инкапсуляцию со стороны выглядит просто как композиция из отдельных обьектов.
Re: Зачем нужны иерархии реализаций?
От: L.C.R. Россия lj://_lcr_
Дата: 03.07.05 23:40
Оценка:
Alexey Chen,

AC>Зачем нужны иерархии реализаций? Не абстракных интерфейсов, а именно полноценных реализаций. Это когда конкретные классы публично наследуются.


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

Отличия весьма разношёрстные, например понятие "бесконечность" у версий 0 и 1 — это 999999 , а у остальных — это 0! Ещё пример: в версии 0 нет криптования, в версиях 1 и 2 — 128 бита, а версиях 4 и 5 — 256. Есть ещё алгоритмические отличия, но в принципе то же самое — до какой то версии обработка одним способом, а после — другим.

Полученная картинка очень органично укладывается в наследование реализации.
interface Strategy
{
  int           getInfinity();
  EncryptParams getEncryptParams();
  Path          getPath(Graph g);
}
///////////////////////////////////////////////////////////////////////////
// все эти классы содержатся в одном файле, что удобно при сопровождении //
///////////////////////////////////////////////////////////////////////////
class Strategy0 implements Strategy
{
  int           getInfinity() { return 999999;}
  EncryptParams getEncryptParams() { return new EncryptParams(0);}
  Path          getPath(Graph g)
  {
    // get path, the graph actually must be tree
    // return null, if it is not a tree or it is not connected
    // else return path needed
  }
}

class Strategy1 extends Strategy0
{
  EncryptParams getEncryptParams() { return new EncryptParams(128);}
}

class Strategy2 extends Strategy1
{
  int           getInfinity() { return 0;}
  Path          getPath(Graph g)
  {
    // get path, the graph can be any but must not have negative cycle
    // return null, if it is not a such graph or it is not connected
    // else return path needed
  }
}

class Strategy3 extends Strategy2
{
  EncryptParams getEncryptParams() { return new EncryptParams(256);}
}

class Strategy4 extends Strategy3
{
}



AC>Как бы исчерпывающего ответа я не знаю. Так, на пальцах и небольших примерах. Может кто-то обьяснит это фундаментально?


Фундаментально? Это просто альтернатива методу Yank+Paste, глубже копать не имеет смысла.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.