Как реорганизовать дубликаты кода обработки событий

У меня есть следующий класс, который позволяет определенным объектам подписаться на событие изменения. Проблема в том, что у меня также есть классы B и C, которые нуждаются в этой функциональности, которая позволяет объектам подписываться на такие же вещи. Мы, конечно, не хотим копировать и вставлять это поведение.

Мы рассмотрели наследование от общего базового класса, но все наши классы, включая A, B и C, уже наследуют от общего BaseClass. И мы не хотим добавлять это поведение в BaseClass, потому что другие наши классы E,F,G, которые наследуются от BaseClass, не нуждаются в этом поведении.

Есть ли лучшее решение?

public class A : BaseClass
{

    /*other properties and code */

    public event EventHandler OnChange;
    private bool _hasChanged;
    public bool HasChanged
    {
        get { return _hasChanged; }
        set
        {
            _hasChanged = value;
            //only need to notify when we've changed.
            if (value)
            {
                if (OnChange != null)
                    OnChange(this, EventArgs.Empty);
            }
        }
    }
}

4 ответа

Решение

Что если мы не будем использовать наследование на мгновение?

Предположим, вместо того чтобы наследовать от общего базового класса, составьте свой клиентский класс, для которого требуется механизм событий, с объектом, реализующим механизм событий.

Предположим, наш класс

 public class EventNotifier
{
    public event EventHandler OnChange;
    private bool _hasChanged;
    public bool HasChanged
    {
        get { return _hasChanged; }
        set
        {
            _hasChanged = value;
            //only need to notify when we've changed. 
            if (value)
            {
                if (OnChange != null)
                    OnChange(this, EventArgs.Empty);
            }
        }
    }
}

2-

 public class A
{
    private EventNotifier eventNotifier;
    public EventNotifier MyEventNotifier { get { return eventNotifier; } }

    public A()
    {
        eventNotifier = new EventNotifier();
    }


}

3- Теперь ваши пользователи класса A (класс, который унаследован / составлен класс A)

это для, если B содержит A

 public class b
{
    A obj ;
    public b()
    {
        obj = new A();
        obj.MyEventNotifier.OnChange += new EventHandler(delegate { Console.WriteLine("Hi"); });
        obj. MyEventNotifier.HasChanged = true;
    }
}

Рассмотрим подход аспектно-ориентированного программирования, подобный тому, который использовался в этом примере PostSharp. Это позволит вам внедрить такой тип шаблонного кода с использованием атрибутов.

Если вы создали соответствующий аспект, вы могли бы иметь такой код:

public class A : BaseClass
{
    public event EventHandler OnChanged;

    [ChangedNotify("OnChanged")]
    public bool HasChanged { get; set; }
}

или, если идея состоит в том, чтобы иметь один OnChange событие для нескольких свойств, вы можете просто жестко закодировать это в аспекте, сократив ваш код до

public class A : BaseClass
{
    [NotifyOnChanged]
    public bool HasChanged { get; set; }
}

Вы могли бы рассмотреть введение промежуточного класса между BaseClass и A,B,C, который содержит общее поведение. Таким образом, вы не будете загрязнять E,F,G, которые не нуждаются в поведении.

             BaseClass
  -----------------------------
  |                           |
-----                   NotifyBaseClass
E,F,G                         |
                            -----
                            A,B,C

NB. Несмотря на то, что AOP выглядит аппетитно, у меня были серьезные проблемы при попытке заставить Postsharp работать с другими технологиями, такими как MS Code Analysis и MSBuild.

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

Когда вы запускаете, вы просто проверяете, является ли это IChangeable (или чем-то еще), и затем подключаетесь только к событию.

Другие вопросы по тегам