Как реорганизовать дубликаты кода обработки событий - PullRequest
4 голосов
/ 23 ноября 2010

У меня есть следующий класс, который позволяет определенным объектам подписываться на событие изменения.Проблема в том, что у меня также есть классы 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 ]

4 голосов
/ 23 ноября 2010

Рассмотрим аспектно-ориентированный подход к программированию, подобный тому, который использовался в этом 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; }
}
2 голосов
/ 23 ноября 2010

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

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

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

 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;
    }
}
1 голос
/ 23 ноября 2010

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

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

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

0 голосов
/ 23 ноября 2010

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

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

...