Каждый раз так часто я сталкиваюсь с тем случаем, когда я хочу, чтобы в коллекции классов существовала аналогичная логика. Например, возможно, мне нужны как Bird
, так и Airplane
, чтобы иметь возможность Fly()
. Если вы думаете "шаблон стратегии", я бы согласился, но даже со стратегией иногда невозможно избежать дублирования кода.
Например, скажем, применимо следующее (и это очень похоже на реальную ситуацию, с которой я недавно столкнулся):
- Оба
Bird
иAirplane
должны содержать экземпляр объекта, который реализуетIFlyBehavior
. - Оба
Bird
иAirplane
должны запрашивать экземплярIFlyBehavior
Fly()
при вызовеOnReadyToFly()
. - Оба
Bird
иAirplane
должны запрашивать экземплярIFlyBehavior
Land()
при вызовеOnReadyToLand()
. -
OnReadyToFly()
иOnReadyToLand()
являются частными. -
Bird
наследуетAnimal
иAirplane
наследуетPeopleMover
.
Теперь предположим, что позже мы добавим Moth
, HotAirBalloon
и еще 16 объектов, и пусть все они следуют одному и тому же шаблону.
Теперь нам понадобится 20 копий следующего кода:
private IFlyBehavior _flyBehavior;
private void OnReadyToFly()
{
_flyBehavior.Fly();
}
private void OnReadyToLand()
{
_flyBehavior.Land();
}
Две вещи, которые мне не нравятся:
-
Это не очень DRY (одни и те же девять строк кода повторяются снова и снова). Если мы обнаружили ошибку или добавили
BankRight()
вIFlyBehavior
, нам нужно было бы распространить изменения на все 20 классов. -
Нет никакого способа обеспечить, чтобы все 20 классов последовательно выполняли эту повторяющуюся внутреннюю логику. Мы не можем использовать интерфейс, потому что интерфейсы разрешают только публичные члены. Мы не можем использовать абстрактный базовый класс, потому что объекты уже наследуют базовые классы, а С# не допускает множественное наследование (и даже если классы еще не наследовали классы, мы могли бы позже добавить новое поведение, которое реализует, скажем,
ICrashable
, поэтому абстрактный базовый класс не всегда будет жизнеспособным решением).
Что делать, если...?
Что делать, если у С# была новая конструкция, например pattern
или template
или [заполните вашу идею здесь], которая работала как интерфейс, но разрешала вам вводить частные или защищенные модификаторы доступа для членов? Вам все равно необходимо обеспечить реализацию для каждого класса, но если ваш класс реализовал шаблон PFlyable
, у вас есть хотя бы один способ обеспечить, чтобы каждый класс имел необходимый шаблонный код для вызова Fly()
и Land()
. И с помощью современной среды разработки, такой как Visual Studio, вы сможете автоматически генерировать код с помощью команды "Выполнять шаблон".
Лично я считаю, что имеет смысл просто расширить смысл интерфейса для покрытия любого контракта, будь то внутренний (закрытый/защищенный) или внешний (публичный), но я предложил сначала добавить совершенно новую конструкцию, потому что люди, похоже, быть очень категоричным относительно значения слова "интерфейс", и я не хотел, чтобы семантика стала фокусом ответов людей.
Вопросы:
Независимо от того, как вы это называете, я хотел бы знать, имеет ли смысл предлагаемая здесь функция. Нужно ли нам каким-то образом обрабатывать случаи, когда мы не можем абстрагировать столько кода, сколько хотим, из-за необходимости ограничительных модификаторов доступа или по причинам, не зависящим от управления программным обеспечением?
Обновление
Из комментария AakashM, я считаю, что уже есть имя для функции, которую я запрашиваю: Mixin. Итак, я думаю, мой вопрос можно сократить до: "Если С# разрешить Mixins?"