2009-08-17 1 views
35

У меня возникла проблема с моей иерархией классов в приложении WPF. Это одна из тех проблем, при которой у вас есть два дерева наследования, объединяющихся друг с другом, и вы не можете найти логического способа сделать ваше наследование работать гладко без множественного наследования. Мне интересно, есть ли у кого-нибудь яркие идеи для работы такой системы, не делая этого невозможным для отслеживания или отладки.Какие хорошие альтернативы множественному наследованию в .NET?

Я инженер низкого уровня, поэтому моя первая мысль всегда: «О, я просто напишу некоторые из этих классов на родном C++ и ссылаюсь на них извне! Тогда у меня может быть вся моя старая школа Оо весело! " Увы, это не помогает, когда вам нужно наследовать от управляемых элементов управления ...

Позвольте мне показать фрагмент моей текущей диаграммы проецируется класса:

____________________________________  _____________________________________ 
| CustomizableObject     | | System.Windows.Controls.UserControl | 
|____________________________________| |_____________________________________| 
| string XAMLHeader()    |      ▲ 
| string XAMLFooter()    |◄--┐     | 
| CustomizableObject LoadObject() | \     | 
| <Possible other implementations> | \     | 
|____________________________________|  \     | 
     ▲      ▲   \     | 
     |      |   \    | 
     |      |    \    | 
_________________ ______________________ \ _____________________ 
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | 
|_________________| |______________________|  |_____________________| 
                 ▲    ▲ 
                 |    | 
                 |    | 
                ________ _____________ 
               | Sprite | | SpriteFrame | 
               |________| |_____________| 

Проблема довольно ясно: разделение деревьев CustomizableObject и CustomizableControl --- и вставка UserControl в один, но не оба из деревьев.

Не имеет смысла перемещать реализацию CustomizableObject в свои производные классы, поскольку реализация не зависит от класса. Кроме того, было бы очень сложно использовать его несколько раз. Поэтому я действительно не хочу, чтобы интерфейс CustomizableObject. Решение для интерфейса не имеет для меня никакого смысла. (Интерфейсы имеют никогда действительно имело для меня смысл, если честно ...)

Итак, я говорю еще раз, у кого есть какие-то яркие идеи? Это настоящий рассол. Я хотел бы узнать больше о том, как заставить интерфейсы работать с моим деревом объектов, а не против него. Я делаю этот простой сценарий sprite с помощью WPF и C# в качестве прочного упражнения, больше всего на свете. Это так легко решить на C++ - но мне нужно выяснить, как решить эти проблемы в управляемой среде, а не бросать мои руки в воздух и бежать обратно к Win32 всякий раз, когда становится жесткой.

+19

+1 для рисования все это :) – Kamarey

+1

Ха-ха, я рад, что мои работы можно оценить. – Giffyguy

+1

Этот вопрос не вписывается в формат Q & A stackoverflows. «Какие хорошие альтернативы» уже предполагает, что нет единого правильного ответа. Там много разных способов и мнений. –

ответ

17

Один из подходов заключается в использовании методов расширения с интерфейсом для обеспечения реализации «производного класса», как и System.Linq.Queryable:

interface ICustomizableObject 
{ 
    string SomeProperty { get; } 
} 

public static class CustomizableObject 
{ 
    public static string GetXamlHeader(this ICustomizableObject obj) 
    { 
     return DoSomethingWith(obj.SomeProperty); 
    } 

    // etc 
} 

public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject 
{ 
    public string SomeProperty { get { return "Whatever"; } } 
} 

Использование: Пока у вас есть, используя директиву (или находятся в том же пространстве имен) пространство имен, где определяются ваши методы расширения:

var cc = new CustomizableControl(); 
var header = cc.GetXamlHeader(); 
+0

Добавлено использование пример для вас. Пока компилятор может их найти, методы расширения выглядят так же, как методы экземпляров. К сожалению, нет свойств расширения, поэтому я переименовал этот метод в соответствие с соглашениями об именах методов. – dahlbyk

+4

Также ознакомьтесь с этой статьей в MSDN: http://msdn.microsoft.com/en-us/vcsharp/bb625996.aspx – dahlbyk

+0

Это решение идеально решает проблему! Это действительно здорово. Бонус здесь заключается в том, что я должен поддерживать реализацию в одном месте, в моем дереве объектов, вместо того, чтобы распространять его по полиморфно или с помощью интерфейсов, поэтому он отлично работает с менталитетом управления WPF. Я рекомендую это решение всем, кто имеет проблемы с множественным наследованием/интерфейсом в .NET. – Giffyguy

1

Похоже, вам придётся прибегать к интерфейсам и композиции объектов.

29

У вас есть два варианта: использовать интерфейсы или использовать композицию. Честно говоря, интерфейсы очень мощные, и после прочтения этой строки

Решение интерфейса не имеет для меня никакого смысла. (Интерфейсы никогда не имели для меня особого смысла, если честно ...)

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

+0

Мне очень нравится, как вы это описываете. Кажется, я, наверное, возьму маршрут «композиции». Это будет усложнять вещи, но не так, как мои реализации повсюду. (Ха-ха, я так упрям ​​... Я прикладываю все усилия, чтобы спросить SO непосредственно о помощи с интерфейсами, и я НЕ ЕЩЕ не могу проглотить свою гордость и принять все советы о мощности интерфейсов.) – Giffyguy

7

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

Вы можете достичь этого через делегатов. Я не уверен, какие именно члены дают вам неприятности, но, возможно, что-то больше, как это:

____________________________________  _____________________________________ 
| ICustomizable      | | System.Windows.Controls.UserControl | 
|         | |_____________________________________| 
| Func<string> XAMLHeader;   |      ▲ 
| Func<string> XAMLFooter   |◄--┐     | 
| ICustomizabl LoadObject() | \     | 
| <Possible other implementations> | \     | 
|____________________________________|  \     | 
     ▲      ▲   \     | 
     |      |   \    | 
     |      |    \    | 
_________________ ______________________ \ _____________________ 
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | 
|_________________| |______________________|  |_____________________| 
                 ▲    ▲ 
                 |    | 
                 |    | 
                ________ _____________ 
               | Sprite | | SpriteFrame | 
               |________| |_____________| 

Кроме того, вы, вероятно, есть какая-то логика, которая действительно статичным, что вы чувствуете на самом деле принадлежит с вашим типом CustomizableObject. Но это, вероятно, неверно: вы создали тип с намерением использовать этот тип в конкретной ситуации. Например, из контекста, похоже, вы создадите эти элементы управления и анимации и используйте их в форме Windows. Дело в том, что у вас есть собственная форма, которая наследуется от базового System.Windows.Form, и этот новый тип формы должен знать об ICustomizableObject и как его использовать. Вот где будет ваша статическая логика.

Это кажется немного неудобным, но оно доказало свою точность, когда вы решили изменить двигатели презентаций. Что произойдет, если вы переносите этот код в WPF или Silverlight? Скорее всего, они будут использовать ваш код реализации немного иначе, чем Windows Forms, и вам все же, вероятно, придется изменить свои реализации CustomizableControl. Но ваша статическая логика теперь находится в правильном месте.

И, наконец, метод LoadObject(), который вы используете, выделяется мне как не в том месте. Вы говорите, что хотите, чтобы каждый настраиваемый тип предоставлял метод, который вы можете назвать, который знает, как загрузить/построить сам. Но это действительно что-то другое. Возможно, вам понадобится еще один интерфейс с именем IConstructable<T>, и у вас есть свой тип реализации, подобный ICustomizable, который (IConstructable<ICustomizable>).

+0

+1 Хмммм ... вы поднимаете очень интересный момент. Я бы очень хотел, чтобы мои спрайт-элементы были автономными. В конце концов, это WPF, поэтому я, вероятно, не должен просто наследовать тип базового окна, как в WinForms, но я мог бы легко применить это решение, скажем, к моим элементам управления SpriteLayer или SpriteView. – Giffyguy

+1

Должен сказать, мне очень нравится подход делегата. Я действительно потратил некоторое время на кодирование, пытаясь понять, как делегаты будут работать для этого. Я планировал принять ваш ответ, как только я получил его для компиляции и запуска - методы расширения только улучшились, и я думаю, что чище.Тем не менее, это, безусловно, отличное решение, достойное внимания. На самом деле, я готов поспорить, что существует множество случаев, когда делегаты решат проблему еще лучше, чем методы расширения. – Giffyguy

1

Я использую mixins в таком случае. mixins - это мощная концепция, используемая на многих языках для добавления функциональности в класс во время выполнения. mixins хорошо известны на многих языках. Структура re-mix - это структура, которая привносит технологию mixin в .NET.

Идея проста: Украсьте свой класс атрибутом класса, который представляет собой mixin. Во время выполнения эта функциональность будет добавлена ​​в ваш класс. Таким образом, вы можете эмулировать множественное наследование.

Решение проблемы может заключаться в том, чтобы сделать CustomizableObject для mixin. Для этого вам понадобится структура re-mix.

[Uses (CustomizableObjectMixin)] CustomizableControl: UserControl

Пожалуйста, ознакомьтесь с remix.codeplex.com для получения более подробной информации.

1

Я думаю, что это решение.

public interface IClassA 
{ 
    void Foo1(); 
    void Foo2(); 
} 

public interface IClassB 
{ 
    void Foo3(); 
    void Foo4(); 
} 

public class ClassA :IClassA 
{ 
    #region IClassA Members 

    public void Foo1() 
    { 
    } 

    public void Foo2() 
    { 
    } 

    #endregion 
} 

public class ClassB :IClassB 
{ 
    #region IClassB Members 

    public void Foo3() 
    { 
    } 

    public void Foo4() 
    { 
    } 

    #endregion 
} 

public class MultipleInheritance :IClassA, IClassB 
{ 
    private IClassA _classA; 
    private IClassB _classB; 

    public MultipleInheritance(IClassA classA, IClassB classB) 
    { 
     _classA = classA; 
     _classB = classB; 
    } 

    public void Foo1() 
    { 
     _classA.Foo1(); 
    } 

    public void Foo2() 
    { 
     _classA.Foo2(); 
     AddedBehavior1(); 
    } 

    public void Foo3() 
    { 
     _classB.Foo3(); 
     AddedBehavior2(); 
    } 

    public void Foo4() 
    { 
     _classB.Foo4(); 
    } 

    private void AddedBehavior1() 
    { 

    } 

    private void AddedBehavior2() 
    { 

    } 
} 

Класс MultipleInheritance добавляет новое поведение к двум различным объектам, не влияя на поведение этих объектов. MultipleInheritance имеют такое же поведение, как и поставленные объекты (наследует оба поведения). Сори для моего английского. С уважением.

+0

Я не вижу это как узор декоратора. –

+0

Я думаю, что это действительный и возможный подход в контексте _some_, я не понимаю, почему вы были заблокированы. Очевидно, что это не общий подход, поскольку декораторы предназначены для расширения существующих классов. Хотя вы можете напрямую использовать стиль декоратора и добавить к нему совершенно новые методы. Я могу видеть, что это не шаблон декоратора, но он может работать как вариация композиции. – julealgon

 Смежные вопросы

  • Нет связанных вопросов^_^