ОО принцип: c #: дизайн для интерфейса, а не конкретные классы - PullRequest
2 голосов
/ 27 августа 2011

У меня есть несколько вопросов о влиянии использования конкретных классов и интерфейсов.

  1. Скажем, часть кода (назовите его chunkCode) использует конкретный класс A. Должен ли я пересобрать chunkCode, если:

    1. Я добавляю несколько новых открытых методов в A? Если так, разве это не немного странно? В конце концов, я все еще предоставляю интерфейс, на который опирается chunkCode. (Или мне придется перекомпилировать, потому что chunkCode может никогда не узнать, что это правда, и я не опустил некоторые API)
    2. Я добавляю несколько новых приватных методов в A?
    3. Я добавляю новое открытое поле в A?
    4. Я добавляю новое личное поле в A?
  2. Шаблон дизайна фабрики: Основному коду не важно, какой конкретно тип объекта. Он опирается только на API. Но что бы вы сделали, если бы было несколько методов, относящихся только к одному конкретному типу? Этот тип реализует интерфейс, но добавляет еще несколько открытых методов? Вы бы использовали if (A is type1) операторов (или тому подобное) основной код?

Спасибо за любые разъяснения

Ответы [ 3 ]

4 голосов
/ 27 августа 2011

1) Компиляция не выполняется в OO.Это деталь конкретных реализаций ОО.Если вы хотите получить ответ для конкретной реализации (например, Java), вам нужно уточнить.

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

Редактировать : вы указали C #, поэтому проверьте этот вопрос, касающийся критических изменений в .Net .Я не хочу делать это, отвечая на медвежью услугу, поэтому я не буду пытаться повторить это здесь.

2) Люди часто взламывают свои замыслы, чтобы сделать это, но этопризнак того, что у вас плохой дизайн.

Хорошие альтернативы :

  • Создайте метод в своем интерфейсе, который позволит вам вызывать пользовательское поведение,но не обязательно знать, что это за поведение.

  • Создать дополнительный интерфейс (и новую фабрику), поддерживающий новые методы.Новый интерфейс не должен наследовать старый интерфейс, но может, если это имеет смысл (если между интерфейсами может быть выражено отношение is-*).

  • Если ваш язык поддерживаетиспользуйте шаблон Abstract Factory и воспользуйтесь Ковариантными типами возврата на бетонном заводе.Если вам нужен конкретный производный тип, примите конкретный завод вместо абстрактного.

Плохие альтернативы (анти-шаблоны):

  • Добавление метода к интерфейсу, который ничего не делает в других классах производных.

  • Создание исключения в методе, который не имеет смысла для вашего производного класса.

  • Добавление методов запроса в интерфейс, которые сообщают пользователю, могут ли они вызвать определенный метод.

Если имя метода не является достаточно универсальным, чтобыпользователь не будет ожидать, что он что-то сделает (например, DoExtraProcessing), тогда добавление метода, который не работает в большинстве производных классов, нарушает контракт, определенный этим интерфейсом.

Например: Кто-то вызывает bird.Fly()ожидал бы, что это на самом деле сделать что-то.Мы знаем, что цыплята не могут летать.Так что либо Chicken не является Bird, либо Bird s не Fly.

Добавление методов запроса является плохим обходным путем.Например, добавив метод или свойство boolean CanFly() в ваш интерфейс.Так бросает исключение.Никто из них не обходит стороной тот факт, что тип просто не заменяется.Ознакомьтесь с принципом замены Лискова (LSP).

0 голосов
/ 27 августа 2011

Вопрос 1: Зависит от того, на каком языке вы говорите.Хотя всегда безопаснее перекомпилировать оба языка.Главным образом потому, что chuckCode не знает, что на самом деле существует внутри A. Перекомпиляция обновляет его память.Но это должно работать в Java без перекомпиляции.

Вопрос 2: Нет. Весь смысл написания Фабрики состоит в том, чтобы избавиться от if (A - type1) .Эти заявления ужасны с точки зрения обслуживания.

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

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

0 голосов
/ 27 августа 2011

Для вашего первого вопроса ответ НЕТ для всех ваших баллов.Если бы это было так, обратная совместимость не имела бы никакого смысла.Вы должны перекомпилировать chunkCode, только если вы тормозите API, то есть удалите некоторые функции, которые использует chunkCode, измените соглашения о вызовах, измените количество параметров, такие вещи == прерывание изменений.

Дляво-вторых, я обычно, но только если мне действительно нужно, использовать dynamic_cast в таких ситуациях.

Примечание мой ответ действителен в контексте C ++; я только что увидел вопрос о языкеагностик (устал в этот час; я уберу ответ, если он кого-нибудь обидит).

...