Время от времени я сталкиваюсь со случаем, где я хочу, чтобы набор классов все обладал подобной логикой. Например, возможно, я хочу обоих a 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 (те же девять строк кода повторяются много раз). Если мы обнаружили ошибку или добавили a BankRight()
кому: IFlyBehavior
, нам были бы нужны к propogate изменения во всех 20 классах.
Нет никакого способа осуществить те все 20, классы последовательно реализуют эту повторяющуюся внутреннюю логику. Мы не можем использовать интерфейс, потому что интерфейсы только разрешают общедоступным участникам. Мы не можем использовать абстрактный базовый класс, потому что объекты уже наследовали базовые классы, и C# не позволяет множественное наследование (и даже если бы классы уже не наследовали классы, то мы могли бы позже хотеть добавить новое поведение, которое реализует, скажем, ICrashable
, таким образом, абстрактный базовый класс не всегда будет эффективным решением).
Что, если...?
Что, если C# имел новую конструкцию, сказать pattern
или template
или [заполняют Вашу идею здесь], который работал как интерфейс, но позволил Вам помещать модификаторы частного или защищенного доступа на участников? Необходимо было бы все еще обеспечить реализацию для каждого класса, но если класс, реализованный PFlyable
шаблон, у Вас, по крайней мере, был бы способ осуществить тот каждый класс, имел необходимый шаблонный код для вызова Fly()
и Land()
. И с современным IDE как Visual Studio Вы смогли бы автоматически сгенерировать код с помощью команды "Implement Pattern".
Лично, я думаю, что имело бы больше смысла просто разворачивать значение интерфейса покрыть любой контракт, не хотел ли внутренний (частный/защищающий) или внешний (общественность), но я предложил добавить совершенно новую конструкцию сначала, потому что люди, кажется, очень непреклонны по отношению к значению слова "интерфейс", и я, чтобы семантика стала фокусом ответов людей.
Вопросы:
Независимо от того, что Вы называете им, я хотел бы знать, имеет ли функция, которую я предлагаю здесь, смысл. Нам нужен некоторый способ обработать случаи, где мы не можем абстрагировать далеко столько кода, сколько мы хотели бы, из-за потребности в строгих модификаторах доступа или по причинам за пределами управления программиста?
Обновление
Из комментария AakashM я полагаю, что уже существует название функции, которую я запрашиваю: Mixin. Так, я предполагаю, что мой вопрос может быть сокращен к: "C# должен разрешить Mixins?"
Описанную вами проблему можно решить с помощью шаблона "Посетитель" (все можно решить с помощью шаблона "Посетитель", будьте осторожны!)
Шаблон "Посетитель" позволяет вы перемещаете логику реализации к новому классу. Таким образом, вам не нужен базовый класс, а посетитель отлично работает с разными деревьями наследования.
Подводя итог:
Для справки см. Шаблон посетителей
Мы не можем использовать методы расширения для этого
public static void OnReadyToFly(this IFlyBehavior flyBehavior)
{
_flyBehavior.Fly()
}
Это имитирует желаемую функциональность (или миксины)
Visual Studio уже предлагает это в «бедной форме» с фрагментами кода. Кроме того, с инструментами рефакторинга в стиле ReSharper (и, возможно, даже с собственной поддержкой рефакторинга в Visual Studio) вы значительно продвинетесь в обеспечении согласованности.
[РЕДАКТИРОВАТЬ: Я не думал о методах расширения, этот подход продвигает вас еще дальше (вам нужно только сохранить _flyBehaviour как частную переменную). Это делает остальную часть моего ответа, вероятно, устаревшей ...]
Однако; просто ради обсуждения: как это можно улучшить? Вот мое предложение.
Можно представить себе что-то вроде следующего, которое будет поддерживаться будущей версией компилятора C #:
// keyword 'pattern' marks the code as eligible for inclusion in other classes
pattern WithFlyBehaviour
{
private IFlyBehavior_flyBehavior;
private void OnReadyToFly()
{
_flyBehavior.Fly();
}
[patternmethod]
private void OnReadyToLand()
{
_flyBehavior.Land();
}
}
Что вы могли бы использовать тогда, например:
// probably the attribute syntax can not be reused here, but you get the point
[UsePattern(FlyBehaviour)]
class FlyingAnimal
{
public void SetReadyToFly(bool ready)
{
_readyToFly = ready;
if (ready) OnReadyToFly(); // OnReadyToFly() callable, although not explicitly present in FlyingAnimal
}
}
Будет ли это улучшением? Наверное. Это действительно того стоит? Может быть ...
Вы только что описали аспектно-ориентированное программирование.
Одной из популярных реализаций AOP для C# является PostSharp (основной сайт, похоже, не работает, однако, это прямая страница "About").
В продолжение комментария: Я не уверен, что PostSharp поддерживает это, но я думаю, что вы говорите о этой части AOP:
Межтиповые декларации обеспечивают способ для выражения сквозных проблем влияющие на структуру модулей. Также известные как открытые классы, это позволяет программистам объявлять в одном месте члены или родители другого класса, обычно для того, чтобы объединить весь код, связанный с проблемой, в одном аспекте.
Можете ли вы добиться такого поведения, используя новый ExpandoObject в .NET 4.0?
Я буду использовать методы расширения для реализации поведения, как показано в коде.
Пусть объекты Bird
и Plane
реализуют свойство для объекта IFlyBehavior
для интерфейса IFlyer
открытый интерфейс IFlyer
{
public IFlyBehavior FlyBehavior
}
public Bird: IFlyer
{
public IFlyBehaviour FlyBehavior {get; set;} { {1}}}
public Airplane: IFlyer
{
public IFlyBehaviour FlyBehavior {get; set;}
}
Создайте класс расширения для IFlyer
public IFlyerExtensions
{
public void OnReadyToFly (этот флаер IFlyer)
{{{1 }} flyer.FlyBehavior.Fly ();
}
public void OnReadyToLand (этот флаер IFlyer)
{
flyer.FlyBehavior.Land ();
}
}
Черты Scala были разработаны для решения такого рода сценариев. Также есть некоторые исследования, чтобы включить черт в C # .
ОБНОВЛЕНИЕ: Я создал свой собственный эксперимент, чтобы иметь ролей в C # . Взглянем.