Скорочення котлоагрегату в класі, який реалізує інтерфейси за допомогою композиції


11

У мене клас: Aце складова низки менших класів B, Cі D.

B, CІ Dреалізовувати інтерфейси IB, ICі IDвідповідно.

Оскільки Aпідтримується весь функціонал B, Cі D, Aреалізується IB, ICі IDтак само, але це, на жаль, призводить до безлічі перенаправлень у впровадженніA

Так:

interface IB
{
    int Foo {get;}
}

public class B : IB
{
    public int Foo {get {return 5;}}
}

interface IC
{
    void Bar();
}

public class C : IC
{
    public void Bar()
    {
    }
}

interface ID
{
    string Bash{get; set;}
}

public class D: ID
{
    public string Bash {get;set;}
}

class A : IB, IC, ID
{
    private B b = new B();
    private C c = new C();
    private D d = new D();

    public int Foo {get {return b.Foo}}

    public A(string bash)
    {
        Bash = bash;
    }

    public void Bar()
    {
        c.Bar();
    }

    public string Bash
    {
         get {return d.Bash;}
         set {d.Bash = value;}
    }
}

Чи є спосіб позбутися від будь-якого з цих перенаправлень котла A? B, CІ Dвсе реалізувати різні , але загальні функціональні можливості , і мені подобається , що Aзнаряддя IB, ICі IDтому , що це означає , що я можу передати в Aсебе як залежність зіставлення цих інтерфейсів, і не повинен піддавати внутрішні допоміжні методи.


Не могли б ви сказати трохи про те, для чого вам потрібен клас А, щоб реалізувати IB, IC, ID? Чому б просто не виставити БХД як публічну?
Есбен Сков Педерсен

1
Я думаю, що моя головна причина, що я віддаю перевагу Aвтіленню IB, ICі IDчи вважає це більш розумним писати Person.Walk(), на відміну Person.WalkHelper.Walk(), наприклад.
Нік Уделл

Відповіді:


7

Те, що ви шукаєте, зазвичай називають міксинами . На жаль, C # не підтримує їх.

Обхідних шляхів небагато: один , два , три та багато інших.

Мені насправді дуже подобається останній. Ідея використання автоматично створеного часткового класу для генерації котлової панелі, мабуть, найближча, ви можете дійти до справді хорошого рішення:

[pMixins] - плагін Visual Studio, який сканує рішення для часткових класів, прикрашених атрибутами pMixin. Позначивши свій клас частковим, [pMixins] може створити файл із кодом і додати до свого класу додаткових членів


2

Хоча це не зменшує котельну плиту в самому коді, Visual Studio 2015 тепер пропонує опцію рефакторингу, щоб автоматично генерувати панель котлів для вас.

Для цього спочатку створіть свій інтерфейс IExampleта реалізацію Example. Потім створіть свій новий клас Compositeі зробіть його у спадок IExample, але не реалізуйте інтерфейс.

Додайте властивість або поле типу Exampleдо вашого Compositeкласу, відкрийте меню швидкодіючих дій на маркері IExampleв вашому Compositeфайл класу і виберіть «Реалізувати інтерфейс , через" Приклад "» , де «Приклад» в даному випадку цього імені поля або властивості.

Слід зазначити, що, хоча Visual Studio буде генерувати та перенаправляти всі методи та властивості, визначені в інтерфейсі, до цього хелперного класу, він не буде перенаправляти події на момент опублікування цієї відповіді.


1

Ваш клас робить занадто багато, тому вам доведеться реалізувати стільки котельних плит. Ви кажете в коментарях:

відчувати себе більш розумним писати Person.Walk () на відміну від Person.WalkHelper.Walk ()

Я не погоджуюсь. Акт ходьби, ймовірно, передбачає багато правил і не належить до Personкласу - він належить до WalkingServiceкласу з walk(IWalkable)методом, за допомогою якого Особа реалізує IWalkable.

Завжди пам’ятайте про принципи SOLID, коли ви пишете код. Найбільш застосовні тут два розділення проблем (вилучення коду виходу до окремого класу) та розділення інтерфейсів (розділення інтерфейсів на конкретні завдання / функції / здібності). Здається, що у вас може бути якийсь I із кількома інтерфейсами, але потім скасовуєте всі ваші добрі роботи, змушуючи один клас реалізувати їх.


У моєму прикладі функціональність буде реалізована в окремих класах, і у мене є клас , що складається з декількох з них , як спосіб угруповання необхідного поводження. Жодна реальна реалізація не існує в моєму великому класі, все це обробляється меншими компонентами. Можливо, ти маєш рацію, і зробити так, щоб зробити ці класи помічників загальнодоступними, щоб з ними можна було безпосередньо взаємодіяти.
Нік Уделл

4
Що стосується ідеї walk(IWalkable)методу, мені особисто це не подобається, оскільки тоді послуги з ходьби стають відповідальністю абонента, а не Особи, і послідовність не гарантується. Будь-який абонент повинен знати, яку службу використовувати для гарантування послідовності, тоді як зберігання її в класі Person означає, що її потрібно вручну змінити, щоб поведінка при ходьбі відрізнялася, все ж допускаючи інверсію залежності.
Нік Уделл

0

Те, що ви шукаєте, - це множинне спадкування. Однак ні C #, ні Java не мають цього.

Ви можете продовжити B від A. Це позбавить від необхідності приватного поля для B, а також клею для перенаправлення. Але зробити це можна лише для одного з B, C або D.

Тепер, якщо ви можете зробити C у спадок від B, а D від C, тоді вам потрібно мати лише розширення D ...;) - але для того, щоб було зрозуміло, я насправді не заохочую це в цьому прикладі, оскільки немає ознак для нього.

У C ++ ви можете мати спадщину від B, C та D.

Але багаторазове успадкування робить програми важче міркувати і має свої проблеми; Крім того, часто існує чистий спосіб уникнути цього. І все-таки іноді без цього потрібно робити дизайнерські компроміси між повторюваною плитою котла та тим, як піддається об'єктна модель.

Це трохи схоже на те, що ви намагаєтесь змішати склад і спадщину. Якщо це був C ++, ви могли використовувати чисте рішення про спадкування. Але оскільки це не так, я б рекомендував використовувати композицію.


2
Ви можете мати багатократне успадкування, використовуючи інтерфейси як у java, так і в c #, так, як це робив op у своєму прикладі коду.
Роберт Харві

1
Деякі можуть вважати реалізацію інтерфейсу таким же, як і багатократне успадкування (як, наприклад, у C ++). Інші не погоджуються, тому що інтерфейси не можуть представляти успадковані методи екземплярів, як це було б у вас, якщо у вас є багатократне успадкування. У C # ви не можете розширити кілька базових класів, отже, ви не можете успадкувати реалізацію методу екземпляра з декількох класів, саме тому вам потрібні всі
котлети

Я погоджуюсь, що багатократне успадкування - це погано, але альтернатива C # / Java однократному успадкуванню з багатократною реалізацією інтерфейсу - це не всі засоби (без пересилання інтерфейсу це ергономічний кошмар). Після того , як в даний час провів деякий час з Golang, я думаю , Go отримав правильне уявлення про те, не маючи ніякого спадку і замість того, щоб покладатися виключно на композиції з інтерфейсом переадресацією - але їх конкретна реалізація має проблеми теж. Я думаю, що найкраще рішення (яке, схоже, ще ніхто не реалізував) - це композиція, пересилання, можливість використання будь-якого типу в якості інтерфейсу.
Дай
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.