Время от времени я сталкиваюсь с случаем, когда я хочу, чтобы коллекция классов имела одинаковую логику. Например, может быть, я хочу, чтобы и 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();
}
Две вещи, которые мне не нравятся в этом:
Это не очень СУХОЙ (одни и те же девять строк кода повторяются снова и снова). Если мы обнаружили ошибку или добавили BankRight()
к IFlyBehavior
, нам нужно будет распространить изменения на все 20 классов.
Нет никакого способа добиться того, чтобы все 20 классов последовательно реализовывали эту повторяющуюся внутреннюю логику. Мы не можем использовать интерфейс, потому что интерфейсы разрешают только публичные члены. Мы не можем использовать абстрактный базовый класс, потому что объекты уже наследуют базовые классы, а C # не допускает множественного наследования (и даже если классы еще не наследуют классы, мы можем позже захотеть добавить новое поведение, которое реализует, скажем, ICrashable
, поэтому абстрактный базовый класс не всегда будет жизнеспособным решением).
Что если ...?
Что, если в C # появилась новая конструкция, скажем, pattern
или template
, или [впишите вашу идею здесь], которая работала бы как интерфейс, но позволяла вам добавлять модификаторы частного или защищенного доступа к членам? Вам все еще нужно будет предоставить реализацию для каждого класса, но если ваш класс реализует шаблон PFlyable
, у вас будет хотя бы способ обеспечить, чтобы у каждого класса был необходимый шаблонный код для вызова Fly()
и Land()
. А в современной IDE, такой как Visual Studio, вы сможете автоматически генерировать код с помощью команды «Реализация шаблона».
Лично я думаю, что было бы более целесообразно просто расширить значение интерфейса, чтобы охватить любой контракт, будь то внутренний (частный / защищенный) или внешний (публичный), но я предложил сначала добавить целую новую конструкцию, потому что люди быть очень непреклонным в отношении значения слова «интерфейс», и я не хотел, чтобы семантика стала центром ответов людей.
Вопросы:
Независимо от того, как вы это называете, я хотел бы знать, имеет ли смысл предлагаемая здесь функция. Нужен ли нам какой-то способ обработки случаев, когда мы не можем абстрагироваться от необходимого количества кода из-за необходимости модификаторов ограниченного доступа или по причинам, не зависящим от программиста?
Обновление
Из комментария AakashM я считаю, что уже есть название для функции, которую я запрашиваю: a Mixin . Итак, я думаю, мой вопрос может быть сокращен до: «Должен ли C # разрешать Mixins?»