13

NotifyIcon 및 동적으로 채워진 ContextMenuStrip으로 구성된 UI로 WinForms 응용 프로그램을 구축하고 있습니다. 응용 프로그램을 함께 보관할 수있는 MainForm이 있지만 표시되지는 않습니다.IoC : 이벤트 처리기에 대한 종속성 확보

가능한 한 솔직하게 (객체 그래프를 처리하기 위해 Autofac을 사용하여) 이것을 빌드하고 O 부분과도 잘 어울리는 내 성공에 매우 만족합니다. 현재 구현중인 확장 기능으로 인해 디자인에서 결함을 발견했으며 조금 변형 할 필요가 있습니다. 나는 내가 갈 필요가있는 방법을 알고 있지만 의존성을 정확하게 정의하는 방법에 대해서는 약간 불분명하다고 생각한다.

위에서 언급했듯이 메뉴는 응용 프로그램을 시작한 후에 부분적으로 동적으로 채워집니다. 이를 위해, I는 IToolStripPopulator 인터페이스 정의 : 이것의 구현은 MainForm에 주입

public interface IToolStripPopulator 
{ 
    System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick); 
} 

을하고 Load() 방법은 ContextMenuStrip 폼에 정의 PopulateToolStrip() 핸들러를 호출한다. populator의 종속성은 메뉴 항목에 사용할 데이터를 가져 오는 것과 관련이 있습니다.

이 추상화는 몇 가지 진화 단계를 통해 잘 수행되었지만 둘 이상의 이벤트 핸들러가 필요한 경우 더 이상 충분하지 않습니다. 왜냐하면 양식이 그것과 전혀 관련되어서는 안되기 때문에 하나의 IToolStripPopulator 인터페이스 뒤에 아직도 숨겨져있는 메뉴 항목의 여러 그룹을 생성하기 때문입니다. 좀 더 구체적인 뭔가에 IToolStripPopulator 인터페이스 *를 이름 대신에 주입 그 PopulateToolStrip()의 메소드 EventHandler 매개 변수를 사용하지 않는 새로운 하나를 생성 - 내가 말했듯이

, 나는 내가 좋아하는 일반적인 구조가되어야 하는지를 알고 있다고 생각 객체 (구현 등에서 요구되는 핸들러의 수에 관해 훨씬 더 많은 유연성을 허용한다). 이런 식으로 나의 "제일"IToolStripPopulator은 매우 쉽게 특정 수의 어댑터가 될 수 있습니다.

이제 내가 명확하지 않은 것은 EventHandler 종속성을 해결해야하는 방법입니다. 나는 핸들러가 모두 MainForm에 정의되어야한다고 생각한다. 메뉴 이벤트에 적절하게 반응하는 데 필요한 다른 모든 의존성을 가지기 때문이다. 또한 메뉴를 "소유"한다. 즉, IToolStripPopulator 개체가 결국 MainForm에 삽입되면 Lazy<T>을 사용하여 MainForm 개체 자체에 대한 종속성을 가져와야합니다.

public interface IClickHandlerSource 
{ 
    EventHandler GetClickHandler(); 
} 

이 내 MainForm에 의해 구현되고, 내 특정 IToolStripPopulator 구현 Lazy<IClickHandlerSource>에 종속성을했다 :

내 첫번째 생각은 IClickHandlerSource 인터페이스를 정의했다. 이 방법이 효과가있는 반면 융통성이 없습니다. 잠재적으로 증가하는 핸들러 (MainForm 클래스의 OCP를 심각하게 위반하는)에 대해 별도의 인터페이스를 정의하거나 IClickHandlerSource (주로 ISP 침해)까지 확장해야합니다. 이벤트 핸들러에 대한 의존성을 직접적으로 고려하는 것은 소비자 측에서 좋은 아이디어처럼 보일 수 있지만, 게으른 인스턴스 (또는 이와 유사한 것)의 속성을 통해 생성자를 개별적으로 연결하는 것은 가능하다면 꽤 지저분 해 보입니다.

내 가장 좋은 방법은 현재이 될 것으로 보인다 :

public interface IEventHandlerSource 
{ 
    EventHandler Get(EventHandlerType type); 
} 

인터페이스는 여전히 MainForm에 의해 구현하고 게으른 싱글로 주입 될 것이며, EventHandlerType 내가 필요로하는 다른 유형의 사용자 정의 열거 될 것이다. 이것은 여전히 ​​OCP와 거의 일치하지는 않지만 합리적으로 유연합니다. EventHandlerType은 새 이벤트 처리기 자체 및 새롭게 작성된 IToolStripPopulator의 추가 구현 외에도 해상도 논리가 MainForm 인 것과 같이 새로운 유형의 이벤트 처리기마다 분명히 변경됩니다.

아니면 .... (유일한 목적으로) MainForm에 정의 된 특정 핸들러에 EventHandlerType 옵션 Lazy<MainForm>에 종속 취하고 해결 IEventHandlerSource 별도 구현?

실제로 이벤트 핸들러를 MainForm에서 꺼내는 방법을 생각하려고합니다.하지만 지금 당장은 그렇게 생각할 수 없습니다.

다른 이벤트 핸들러에서 가장 느슨한 커플 링과 가장 세련된 해상도를 제공하는 가장 좋은 옵션은 무엇입니까?

[* 네, 아마 정말 OCP 준수하기 만 이름을 떠난해야하지만 더 나은 그런 식으로 보았다.]

+2

나는 귀하의 질문에 여러 번 읽기를 구현하지만 몇 가지 정보를 누락. 더 많은 코드를 보여줄 수 있습니까? 예를 들어,'IEventHandlerSource'의 소비자는 어떻게 생겼고'IEventHandlerSource'의 구현은 어떻게 보이며, 어떻게 등록되어 있습니까? – Steven

답변

0

당신이 양식 내 자식 구성 요소를 호스팅하는 경우, 그들은 주를 채울 수 있습니다 응용 "쉘".

System.Windows.Forms.ContainerControl에서 상속받은 기본 클래스 ShellComponent을 가질 수 있습니다. 이것은 당신에게 디자인 표면을 줄 것입니다. IToolStripPopulator를 사용하는 대신 ITool을 사용할 수 있습니다.

public inteface ITool<T> 
{ 
    int ToolIndex { get; } 
    string Category { get; } 
    Action OnClick(T eventArgs); 
} 

ShellComponent에서 당신은 MainForm에서, 뷰가로드 될 때마다 public List<ITool> OnAddTools(ToolStrip toolStrip)를 호출 할 수 있습니다. 이렇게하면 구성 요소가 도구 막대를 채우는 데 책임이 있습니다.

'ShellComponent'는 IoC 컨테이너에 ITool<T>을 구현하는 핸들러를 요청합니다. 이 방법으로 ITool<T>은 이벤트를 분리하여 (MainForm 또는 ContainerControl) 이벤트를 분리하고 모든 클래스에 푸시 할 수있는 방법을 제공합니다. 또한, MouseClickEventArgs와는 반대로 인수를 전달할 대상을 정확히 정의 할 수 있습니다.

2

가장 편안한 커플 링과 서로 다른 이벤트 처리기의 가장 높은 해상도를 제공하는 가장 좋은 옵션은 무엇입니까?

일반적인 해결책은 존재하지 않으며 글로벌 응용 프로그램 아키텍처에 따라 다릅니다.프리즘에 http://martinfowler.com/eaaDev/EventAggregator.html

  • 구현 - -

    : 당신이 느슨한 커플 링을 원하는 경우에

    EventAggregator 패턴은 경우에 (당신의 IEventHandlerSource 유사한 것과을) 당신을 도울 수

    그러나 글로벌 이벤트는 과 함께 사용해야합니다. - 그들은 번지 아키텍처를 사용할 수 있습니다. 왜냐하면 이벤트 구독은 어디에서나 가능하기 때문입니다.

    DI 및 IoC에서 중요한 점은 하위 종속성은 상위 종속성에 대해 알지 않아야한다는 것입니다. 레온이 이전에 말한 것처럼 ITool<T>과 같은 인터페이스를 만들고 MainForm에 도구 목록을 저장하는 것이 더 나을 것이라고 생각합니다. 작업 후 MainForm은이 도구에서 특정 메소드를 호출합니다.

  • 1

    첫 번째로, 메인 폼에 모든 이벤트 핸들러가 있어야한다고 주장해서는 안된다고 생각합니다. 당신은 다른 요구 (아마도 다른 의존성)를 가진 다른 핸들러가 있다는 사실에 분명히 부딪쳤다. 그래서 그들은 왜 모두 같은 클래스에 들어야 하는가? 이벤트가 다른 언어로 처리되는 방식에서 영감을 얻을 수 있습니다. WPF는 명령을 사용하고 Java에는 수신기가 있습니다. 두 가지 모두 대리인이 아니라 객체이기 때문에 IOC 시나리오에서 다루기가 더 쉽습니다. 그런 것을 시뮬레이트하는 것은 상당히 쉽습니다. 툴박스 항목의 태그를 악용 할 수 있습니다 (예 : Binding to commands in WinForms). PopulateToolbar (Is there anything wrong with using lambda for winforms event? 참조)의 람다 식을 사용하여 툴바 항목을 올바른 명령과 연결합니다. PopulateToolbar는 생성해야하는 항목을 알고 있기 때문에 각 항목에 속한 작업/명령을 알고 있다고 가정합니다.

    조치를 나타내는 오브젝트는 기본 양식 또는 기타 조치와 독립적으로 자체 종속성을 가질 수 있습니다. 기본 동작이나 기타 동작에 영향을 미치지 않으면 서 자신의 동작이 포함 된 툴바 항목을 나중에 추가하거나 제거 할 수 있으며, 각 동작을 독립적으로 테스트하고 리팩토링 할 수 있습니다.

    결론 : EventHandlers에 대해 생각해보기를 그만 두자. 액션/명령에 대한 생각을 자신의 오른쪽에서 엔티티로 생각하면 적절한 패턴이 나오기 쉬울 것입니다. Command Pattern을 이해했는지 확인하십시오. 여기에 필요한 정보가 많이 있기 때문입니다.

    1

    이벤트 수집기를 사용해 보셨습니까? 참조 : Caliburn framework, event Aggregator 이벤트 수집기는 기본 서식에서 도구 모음을 분리합니다.

    public interface IToolStripPopulator 
    { 
        ToolStrip PopulateToolStrip(ToolStrip toolstrip); 
    } 
    

    랩은이 같은 편의를 위해 애그리 게이터 (aggregator) :

    public static class Event 
    { 
        private static readonly IEventAggregator aggregator = new EventAggregator(); 
        public static IEventAggregator Aggregator 
        { 
         get 
         { 
          return aggregator; 
         } 
        } 
    } 
    

    당신은 예를 들어, 하나 이상의 이벤트 클래스를 정의 : ToolStrip에를 생성 컨트롤러에서

    public class EventToolStripClick { 
        public object Sender {get;set;} 
        public EventArgs Args {get;set;} 
    } 
    

    의 게시 클릭 핸들러의 맞춤 이벤트 작성 :

    public void ControllerToolStripClick(object sender, EventArgs args) 
    { 
        Event.Aggregator.Publish(new EventToolStripClick(){Sender=sender,Args=args)}); 
    } 
    
    mainForm 0

    인터페이스 IHandle

    public class MainForm : Form, IHandle<EventToolStripClick> 
    { 
        ... 
        public void Handle(EventToolStripClick evt) 
        { 
         //your implementation here 
        } 
    } 
    
    관련 문제