성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# 관점에서의 Observer 패턴 구현</h1> <p> 최근에 아래의 글을 읽게 되었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 옵저버 패턴의 이해 - 윈폼예제 ; http://hoons.kr/Lecture/LectureMain.aspx?BoardIdx=48739&kind=26&view=0 </pre> <br /> 사실, 제가 추천해 드리는 것은 저 글 자체보다는 'daniel'님이 그 글에 남긴 질문입니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 설명을 깔끔하게 잘 해주신 것 같습니다. 예제 또한 군더더기 하나 없이 좋네요.. ^^ㅋ 좋은 글 감사합니다. 한가지 질문을 드린다면, 어느 경우에 C#에서 이벤트 말고, Observer 패턴을 쓰는게 효율적인지 문의 드립니다. <br /> </div><br /> <br /> Pattern이란 것들이 대부분 보면, 'interface'에서 출발합니다. 한마디로, 'interface'를 얼마나 다양하게 활용할 지에 대한 숱한 나열을 한 것이 '디자인 패턴'이라고 여겨질 정도입니다.<br /> <br /> 그런데, 이 패턴들이 자주 언급되는 프로그램 언어가 바로 'Java'라는 사실에 주목할 필요가 있습니다.<br /> <br /> 자바는, 언어 자체에 '함수 포인터' 개념이 없기 때문에 이런 부분을 대체해서 구현할 방법이 interface 밖에는 없습니다. 아마도 일반적인 윈도우 개발자들이 자바의 swing 라이브러리를 이용하여 윈도우 프로그램을 할 때 당황스러워하는 것 중의 하나가 이벤트 핸들러를 구현하는 차이라는 점에서 많은 분들이 동의하실 것입니다.<br /> <br /> 예를 들면, 자바에서는 Button의 Click 이벤트에 대해 다음과 같은 식으로 구현을 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class MyFrame extends JFrame <span style='color: blue; font-weight: bold'>implements ActionListener</span> { private JButton button1 = new JButton("Click me!"); private JButton button2 = new JButton("Click me too!"); public MyFrame() { <span style='color: blue; font-weight: bold'>button1.addActionListener(this);</span> <span style='color: blue; font-weight: bold'>button2.addActionListener(this);</span> } <span style='color: blue; font-weight: bold'>public void actionPerformed(ActionEvent evt)</span> { Object src = evt.getSource(); if (src == button1) { ... [Click Event Handler Code] ... } else if (src == button2) { ... [Click Event Handler Code] ... } } } </pre> <br /> 보시는 것처럼, JButton 개체는 addActionListener 메서드를 통해서 '함수 포인터'가 아닌 개체 인스턴스를 받습니다. 그리고, 그 개체 인스턴스는 JDK에 다음과 같이 정의된 ActionListener 인터페이스를 구현하고 있어야 합니다. (닷넷 개발자들은 낯설겠지만, 자바에서는 인터페이스에 "I" 접두사를 붙이는 것이 관례가 아닙니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public interface EventListener { } public interface <span style='color: blue; font-weight: bold'>ActionListener</span> extends EventListener { <span style='color: blue; font-weight: bold'>public void actionPerformed(ActionEvent e);</span> } </pre> <br /> 반면에, 비교를 위해 동일한 목적의 C# 구현예제를 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class MainForm : Form { private Button button1 = new Button("Click me!"); private Button button2 = new Button("Click me too!"); public MyFrame() { button1.Click += Button1_Clicked; button2.Click += Button2_Clicked; } void Button1_Clicked(object sender, EventArgs e) { ... [Click Event Handler Code] ... } void Button2_Clicked(object sender, EventArgs e) { ... [Click Event Handler Code] ... } } </pre> <br /> 이처럼, C#(및 VB.NET 같은 언어들)에서는 '함수 포인터(C#에서는 delegate)' 개념이 있기 때문에 이벤트 핸들러를 구현하기 위해 인터페이스까지 관여시킬 필요가 없습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> swing의 addActionListener를 구현하는 JButton 개체는 내부적으로 Observer 패턴을 구현하고 있는 것입니다. 마찬가지로 C#의 경우에는 button1.Click 이벤트가 Observer 패턴을 구현하고 있는 것이고.<br /> <br /> 이에 대해서 좀 더 들어가볼까요?<br /> <br /> 우선, '함수 포인터'가 있기 때문에 interface 구문을 제거할 수 있다고는 하지만 그것이 전부는 아닙니다. 일례로, C++의 경우에도 함수 포인터는 제공이 되지만 "옵저버 패턴의 이해 - 윈폼예제"에서 보여지는 Add/Remove/Notify에 대한 관리 코드는 별도로 구현을 해주어야 합니다. 즉, interface 키워드만 제거될 뿐 함수 포인터 값에 대한 목록 관리는 Java가 아닌 다른 언어들에서도 동일하게 해주어야 합니다.<br /> <br /> 하지만... C#에서는 그런 작업이 필요하지 않습니다.<br /> <br /> 이제, 'daniel' 님의 질문에 대한 답이 나옵니다. 단적으로 말하면, Observer 패턴은 사실 C# 개발자라면 모른 체로 사용해왔던 개념입니다. 이미 delegate를 사용해 왔다면 그것 자체가 Observer 패턴을 구현한 것이나 다름 없습니다.<br /> <br /> 이해를 돕기 위해... ^^ 잠깐 예제와 함께 설명해 볼까요?<br /> <br /> 아래의 코드는 "옵저버 패턴의 이해 - 윈폼예제"에서 설명한 IObserverCenter 인터페이스입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public void IObserverCenter { void AddObserver(IObserver observer); void RemoveObserver(IObserver observer); void NotifyObservers(int parameter); } </pre> <br /> 그리곤, 내부적으로 Add/Remove한 observer 개체들을 관리하기 위해 IObserverCenter 인터페이스를 다음과 같이 구현해 주고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class CountObserverCenter : IObserverCenter { private List<IObserver> _observerList = new List<IObserver>(); ... [Add/Remove/Notify 구현 생략]... } </pre> <br /> 닷넷에서는 위의 코드에 대한 모든 역할을 아예 프레임워크 자체에 내장하고 있는데 그것이 바로 MulticastDelegate 타입입니다. 그리고, C#에서는 언어적으로 이를 자연스럽게 연동할 수 있도록 delegate 키워드로 정의된 함수 형식을 타입으로 지원하고 있어서 컴파일 시에 MulticastDelegate으로 번역되도록 바꿔줍니다.<br /> <br /> 따라서, 다른 언어에서는 IObserverCenter 인터페이스를 구현해주는 작업을 해야 하지만, C#의 경우에는 그럴 '필요'가 없습니다. 실제로, "옵저버 패턴의 이해 - 윈폼예제" 글에서의 Observer 패턴을 구현한 장황한 예제 코드를 delegate를 사용하여 표현하면 다음과 같이 간단합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public partial class Form1 : Form { public <span style='color: blue; font-weight: bold'>delegate</span> void <span style='color: blue; font-weight: bold'>NumberChanged</span>(int number); public <span style='color: blue; font-weight: bold'>NumberChanged FormNumberChanged; // MulticastDelegate 타입으로 바뀌면서 CountObserverCenter 클래스가 할 일을 모두 내장</span> int _currentNum; public Form1() { InitializeComponent(); _currentNum = 0; Screen1 screenView1 = new Screen1(); <span style='color: blue; font-weight: bold'>this.FormNumberChanged += screenView1.NotifyAction; // IObserverCenter.AddObserver 메서드 역할</span> Screen2 screenView2 = new Screen2(); <span style='color: blue; font-weight: bold'>this.FormNumberChanged += screenView2.NotifyAction;</span> ...[생략]... } private void button1_Click(object sender, EventArgs e) { _currentNum++; if (FormNumberChanged != null) { <span style='color: blue; font-weight: bold'>FormNumberChanged(_currentNum); // IObserverCenter.NotifyObservers 메서드 역할</span> } } } public partial class Screen1 : UserControl // IObserver 인터페이스 상속 없이 NotifyAction 메서드를 그대로 사용 { int _currentNum; public Screen1() { InitializeComponent(); } <span style='color: blue; font-weight: bold'>public void NotifyAction(int parameter) // IObserver.NotifyAction 메서드 역할</span> { _currentNum = parameter; this.label1.Text = string.Format("Screen 1 : {0}", _currentNum); } } public partial class Screen2 : UserControl { ...[생략: Screen1과 유사한 코드 반복]... } </pre> <br /> 훨씬 심플하죠? 이렇게 좋은 delegate를 보고 있자면 굳이 교과서적인 방법으로 Observer 패턴을 구현해야 하는 이유가 모호해집니다.<br /> <br /> 물론, 이럴지라도 interface를 사용해야 할 필요가 나옵니다. 예를 들어, 다른 곳에서 Form의 이벤트를 받고 싶을 때 직접 개체를 참조해야 하는 제약을 끊고 싶은 경우 인터페이스를 이용해야 하기 때문인데요. 그럴 필요가 있다면 다음과 같이 간단하게 인터페이스를 추가해서 정의해 주는 것만으로 해결이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>public interface INumberChanged</span> { NumberChanged FormNumberChanged { get; set; } } public partial class <span style='color: blue; font-weight: bold'>Form1</span> : Form, <span style='color: blue; font-weight: bold'>INumberChanged</span> { ...[생략]... public NumberChanged FormNumberChanged { get; set; } } </pre> <br /> 여기까지... 눈에 들어오시나요? 이처럼, C#에서는 함수 포인터의 지원과 함께 MulticastDelegate 타입을 지원하는 .NET Framework의 도움으로 Observer 패턴 자체가 녹아져 있다고 보시면 됩니다.<br /> <br /> 그렇다고, Observer 패턴의 정석적인 방법을 모르고 사는 것도 좀 그렇지요. ^^ 닷넷을 벗어나면 다시 그렇게 쓸 줄 알아야겠고... 어쨌든 명시적으로 Observer 패턴을 알고 있다면 이해의 범위가 더 커지는 데 도움이 될 것입니다.<br /> <br /> *** 첨부 파일은, "옵저버 패턴의 이해 - 윈폼예제" 글에서 첨부한 <a target='tab' href='http://www.happydong.kr/attachment/cfile1.uf@175870454EA501C02062FD.zip'>예제 프로젝트</a>를 본문에서 설명한 delegate 구문으로 동등하게 바꾼 프로젝트입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1482
(왼쪽의 숫자를 입력해야 합니다.)