C # : 이벤트 또는 관찰자 인터페이스? 장단점?
다음 (간체)이 있습니다.
interface IFindFilesObserver
{
void OnFoundFile(FileInfo fileInfo);
void OnFoundDirectory(DirectoryInfo directoryInfo);
}
class FindFiles
{
IFindFilesObserver _observer;
// ...
}
... 그리고 나는 갈등이 있습니다. 이것은 기본적으로 C ++로 작성했을 것이지만 C #에는 이벤트가 있습니다. 이벤트를 사용하도록 코드를 변경해야합니까? 아니면 그대로 두어야합니까?
기존 옵저버 인터페이스에 비해 이벤트의 장단점은 무엇입니까?
이벤트를 인터페이스에 메서드가 하나만있는 콜백 인터페이스로 간주합니다.
필요한 후크 이벤트 만 이벤트를
사용하면 처리하려는 이벤트에 대한 핸들러 만 구현하면됩니다. 관찰자 인터페이스 패턴에서는 실제로 처리하지 않는 알림 유형에 대한 메서드 본문을 구현하는 것을 포함하여 전체 인터페이스에서 모든 메서드를 구현해야합니다. 귀하의 예제에서는 이러한 이벤트 중 하나에 만 관심이 있더라도 항상 OnFoundDirectory 및 OnFoundFile을 구현해야합니다.
유지 관리 감소
이벤트에 대한 또 다른 좋은 점은 특정 클래스에 새 이벤트를 추가하여 이벤트가 발생하도록 할 수 있으며 기존 관찰자를 모두 변경할 필요가 없다는 것입니다. 인터페이스에 새 메서드를 추가하려면 해당 인터페이스를 이미 구현 한 모든 클래스를 돌아 다니며 모든 클래스에 새 메서드를 구현해야합니다. 하지만 이벤트를 사용하면 추가하는 새 이벤트에 대한 응답으로 실제로 뭔가를 수행하려는 기존 클래스 만 변경하면됩니다.
패턴은 언어에 내장되어 있으므로 모든 사람이 사용 방법을 알 수 있습니다.
이벤트는 관용적입니다. 이벤트를 볼 때 사용 방법을 알 수 있습니다. 관찰자 인터페이스를 사용하면 사람들은 종종 알림을 수신하고 관찰자를 이벤트에 연결하기 위해 다양한 등록 방법을 구현합니다.하지만 일단 등록하고 사용하는 방법 (+ = 연산자 사용)을 배웠 으면 나머지는 모두 같은.
인터페이스에 대한
전문가 인터페이스에 대한 전문가가 많지 않습니다. 나는 누군가가 인터페이스에서 모든 메소드를 구현하도록 강요한다고 생각합니다. 그러나 누군가에게 이러한 모든 방법을 올바르게 구현하도록 강요 할 수는 없으므로 여기에 많은 가치가 있다고 생각하지 않습니다.
구문
어떤 사람들은 각 이벤트에 대해 대리자 유형을 선언하는 방식을 좋아하지 않습니다. 또한 .Net 프레임 워크의 표준 이벤트 핸들러에는 다음 매개 변수가 있습니다. (개체 송신자, EventArgs 인수). 보낸 사람이 특정 유형을 지정하지 않기 때문에 사용하려면 다운 캐스트해야합니다. 정적 유형 시스템의 보호 기능을 잃어 버리기 때문에 옳지 않다고 생각되면 실제로는 괜찮습니다. 그러나 자체 이벤트를 구현하고 이에 대한 .Net 프레임 워크 규칙을 따르지 않는 경우 올바른 유형을 사용할 수 있으므로 잠재적 인 다운 캐스팅이 필요하지 않습니다.
흠, 이벤트를 사용하여 Observer 패턴을 구현할 수 있습니다. 사실, 이벤트를 사용하는 것은 관찰자 패턴 imho의 또 다른 구현으로 간주 될 수 있습니다.
인터페이스 솔루션의 장점 :
- 메서드를 추가하는 경우 기존 관찰자는 해당 메서드를 구현해야합니다. 이는 기존 관찰자를 새로운 기능에 연결하는 것을 잊을 가능성이 적다는 것을 의미합니다. 물론 빈 메서드로 구현할 수 있습니다. 즉, 특정 "이벤트"에 응답하여 아무것도하지 않는 사치가 있음을 의미합니다. 그러나 당신은 쉽게 잊을 수 없습니다.
- 명시 적 구현을 사용하는 경우 다른 방식으로도 컴파일러 오류가 발생합니다. 기존 인터페이스를 제거하거나 변경하면이를 구현하는 관찰자가 컴파일을 중지합니다.
단점 :
- 관찰자 인터페이스의 변경으로 인해 솔루션 전체에 변경 사항이 적용될 수 있으므로 다른 계획이 필요할 수 있으므로 계획에 더 많은 고려가 필요합니다. 간단한 이벤트는 선택 사항이므로 다른 코드가 이벤트에 반응하지 않는 한 다른 코드는 거의 또는 전혀 변경하지 않아도됩니다.
이벤트의 추가 혜택.
- 적절한 멀티 캐스트 동작을 무료로 얻을 수 있습니다.
- 해당 이벤트에 대한 응답으로 이벤트 구독자를 변경하면 동작이 잘 정의됩니다.
- 쉽고 일관되게 성찰 (반영) 할 수 있습니다.
- 이벤트에 대한 도구 체인 지원 (단순히 .net의 관용구이기 때문)
- 제공하는 비동기 API를 사용할 수있는 옵션이 있습니다.
이 모든 것을 (툴 체인 제외) 직접 달성 할 수 있지만 놀랍도록 어렵습니다. 예를 들어 : List <>와 같은 멤버 변수를 사용하여 관찰자 목록을 저장하는 경우. foreach를 사용하여 반복하는 경우 OnFoo () 메서드 콜백 중 하나에서 구독자를 추가하거나 제거하려는 시도는 깔끔하게 처리하기 위해 추가 코드를 작성하지 않는 한 예외를 트리거합니다.
- 예를 들어 FACADE 패턴을 사용하거나 작업을 다른 클래스에 위임하는 경우 이벤트는 개체 체인을 통해 전파하기가 더 어렵습니다.
- 개체가 가비지 수집 될 수 있도록 이벤트 구독을 취소 할 때 매우주의해야합니다.
이벤트는 간단한 함수 호출보다 2 배 느리고, 모든 발생시 null 확인을 수행하는 경우 3 배 느리고, null 확인 및 호출 전에 이벤트 델리게이트를 복사하여 스레드를 안전하게 만듭니다.
이 예를 고려하십시오.
using System;
namespace Example
{
//Observer
public class SomeFacade
{
public void DoSomeWork(IObserver notificationObject)
{
Worker worker = new Worker(notificationObject);
worker.DoWork();
}
}
public class Worker
{
private readonly IObserver _notificationObject;
public Worker(IObserver notificationObject)
{
_notificationObject = notificationObject;
}
public void DoWork()
{
//...
_notificationObject.Progress(100);
_notificationObject.Done();
}
}
public interface IObserver
{
void Done();
void Progress(int amount);
}
//Events
public class SomeFacadeWithEvents
{
public event Action Done;
public event Action<int> Progress;
private void RaiseDone()
{
if (Done != null) Done();
}
private void RaiseProgress(int amount)
{
if (Progress != null) Progress(amount);
}
public void DoSomeWork()
{
WorkerWithEvents worker = new WorkerWithEvents();
worker.Done += RaiseDone;
worker.Progress += RaiseProgress;
worker.DoWork();
//Also we neede to unsubscribe...
worker.Done -= RaiseDone;
worker.Progress -= RaiseProgress;
}
}
public class WorkerWithEvents
{
public event Action Done;
public event Action<int> Progress;
public void DoWork()
{
//...
Progress(100);
Done();
}
}
}
장점은 이벤트가 더 '도트 네티'라는 것입니다. 양식에 놓을 수있는 비 시각적 구성 요소를 디자인하는 경우 디자이너를 사용하여 연결할 수 있습니다.
Cons are that an event only signifies a single event - you need a separate event for each 'thing' that you want to notify the observer about. This doesn't really have much practical impact except that each observed object would need to hold a reference for every observer for every event, bloating memory in the case where there are lots of observed objects (one of the reasons they made a different way of managing the observer/observable relationship in WPF).
In your case I'd argue it doesn't make much difference. If the observer would typically be interested in all those events, use an observer interface rather than separate events.
Java has language support for anonymous interfaces, so callback interfaces are the thing to use in Java.
C# has support for anonymous delegates - lambdas - and so events are the thing to use in C#.
The best way to decide is this: which one suits the situation better. That might sound like a silly or unhelpful answer, but I don't think you should regard one or the other as the "proper" solution.
We can throw a hundred tips at you. Events are best when the observer is expected to listen for arbitrary events. An interface is best when the observer is expected to listed to all of a given set of events. Events are best when dealing with GUI apps. Interfaces consume less memory (a single pointer for multiple events). Yadda yadda yadda. A bulleted list of pros and cons is something to think about, but not a definitive answer. What you really need to do is try both of them in actual applications and get a good feel for them. Then you can choose the one that suits the situation better. Learn form doing.
If you have to use a single defining question, then ask yourself which better describes your situation: A set of loosely related events any of which may be used or ignored, or a set of closely related events which will all generally need to be handled by one observer. But then, I'm just describing the event model and interface model, so I'm back at square one: which one suits the situation better?
A benefit of interfaces is that they are easier to apply decorators to. The standard example:
subject.RegisterObserver(new LoggingObserver(myRealObserver));
compared to:
subject.AnEvent += (sender, args) => { LogTheEvent(); realEventHandler(sender, args); };
(I'm a big fan of the decorator pattern).
I prefer an event base solution for the following reasons
- It reduces the cost of entry. It's much easier to say "+= new EventHandler" than to implement a full fledged interface.
- It reduces maintenance costs. If you add a new event into your class that's all that needs to be done. If you add a new event to an interface you must update every single consumer in your code base. Or define an entirely new interface which over time gets annoying to consumers "Do I implement IRandomEvent2 or IRandomEvent5?"
- Events allow for handlers to be non-class based (ie a static method somewhere). There is no functional reason to force all event handlers to be an instance member
- Grouping a bunch of events into an interface is making an assumption about how the events are used (and it's just that, an assumption)
- Interfaces offer no real advantage over a raw event.
If your objects will need to be serialized in some way that retains references such as with NetDataContractSerializer or perhaps protobuf events will not be able to cross the serialization boundary. Since observer pattern relies on nothing more than just object references, it can work with this type of serialization with no problem if that is what is desired.
Ex. You have a bunch of business objects that link to each other bidirectionally that you need to pass to a web service.
참고URL : https://stackoverflow.com/questions/550785/c-events-or-an-observer-interface-pros-cons
'developer tip' 카테고리의 다른 글
BAT 파일의 "입력 라인이 너무 깁니다"오류 (0) | 2020.12.12 |
---|---|
둘 이상의 행 집합이있는 Django 템플릿의 대체 행 색상 지정 (0) | 2020.12.11 |
PHP & (앰퍼샌드, 비트 및) 연산자 이해 (0) | 2020.12.11 |
필드의 "길이"에 대한 MySQL SELECT 문이 1보다 큽니다. (0) | 2020.12.11 |
Python : dict의 변수를 네임 스페이스로로드 (0) | 2020.12.11 |