'Functional World/C# Design Pattern' 카테고리의 글 목록 :: iopeni - Think of C#


정말 어려운 주제 입니다.. 디펜던시 인젝션이라는 주제는... 이걸 어떻게 설명 해야 할지 참 난감 합니다.


저의 블로그에도 이전에 이 주제를 가지고 한번 포스팅을 한적이 있습니다. 


http://iopeni.tistory.com/entry/Dependency-Injection-Container


Dependency Injection in Action 이라는 책을 보면 DI를 가정집에 전기 콘센트로 비유 하고 있습니다. 뻔한 내용 이겠죠? 헤어드라이든 냉장고든 TV든 콘센트에 꽂혀야 동작을 하니 콘센트는 하나의 가전 제품에 생명력을 불어넣어 주는 ~~ 신비한 곳입니다. 만약 집에 있는 가전 제품들이 모두 특수한 가전제품이라 각기 다른 전기 어댑터를 지니게 된다면, 아마 콘센트의 양은 정말 어마 무시하게 많아 지겠지만, 다행 스럽게 대한민국의 콘센트는 같은 모양을 지니고 있습니다.


이런 일을 컴퓨터 언어적인 측면에서 설명을 하자면, 추상 클래스와 인터페이스 같은 것들로 설명을 할 수 있습니다. 

즉 다형성이라는 주제 인걸로 보입니다. 추상이나 인터페이스의 목적이 좀 모호합니다만, 알고보면 별거 아닙니다.

 

사실 정확하게 이야기 하자면 추상은 인터페이스와 비교하면 목적 자체가 틀립니다. 추상은 콘센트 라기 보다 가전 제품의 종류 입니다. 세상에 에어컨이 있는데 이 에어컨의 종류가 벽면 에어컨도 있고 스탠드 에어컨도 있고 지붕에 달려 있는 에어컨도 있고... 많은 형태의 에어컨이 존재 하지만 모든 에어컨은 공통인 것들 즉 에어컨은 바람이 나오고, 실외기가 있어야 하고, 프레온 가스로 찬 바람을 만들고, 등등 기본적인 성격을 모든 에어컨이 지니게 되는데 이것이 바로 추상 클래스 입니다. 프레온 가스가 있는 이 프레온 가스는 A 회사 제품도 있고, B회사 제품도 있고 다 틀리니 말이죠.


인터페이스는 다릅니다. 


인터페이스는 항구, 공항, 콘센트, 주유소, 세차장, 책장 등과 같습니다. 항구에는 수많은 종류의 배들이 정박 할 수 있는 곳이며, 공항은 수많은 종류의 비행기가 뜨고 내리는 곳입니다. 하나의 객체와 또 다른 객체를 이어주는 연결 고리 라고 생각 하면 됩니다.


물론 이런걸 관점주의적 프로그래밍 이라는 주제로 바라 보면 또 약간의 차이점이 발생 하기도 합니다.


각설하고 말이죠 더 깊에 설명 하려고 하면 골 때리고, 닭이 먼저 인지 달걀이 먼저인지.... 니가 나 인지.. 내가 너 인지 모르는 사태가 발생 할 수 있으므로 우리는 여기서 딱 한가지만 정하고 가야 할 듯 합니다.


오늘은 인터페이스 입니다. 인터페이스 하나만 알면 됩니다.


이걸로 디펜던시 인젝션 이라는 주제를 설명 하고자 합니다. OOP적 관점에서의 인터페이스 라고 하면 이 아무것도 아닌 개념이 정말 난해하게 느껴져 절망을 부르짓고 있는 분이 계실지도 모르겠습니다.


사실 디펜던시 인젝션의 모든 테크닉은 이 인터페이스 에서 비롯 됩니다.


하나의 클래스에서 맴버가 등록 되는데 이 멤버의 타입이 인터페이스 인겁니다. 즉 맴버는 인터페이스를 상속 받은 다양한 클래스들이 이 멤버에 대입 될 수 있으며, 정보를 취득 가공함에 있어 형 변환 연산이 필요하지 않은 것입니다.


마이크로 소프트는 유니티를 다음과 같이 설명 하고 있습니다. https://msdn.microsoft.com/library/ff647202.aspx


오늘 우리는 이 유니티 컨트롤을 사용하는 기초적 용법을 습득하고 다음 포스트에서 다시 프리즘으로 넘어가서 또 다른 주제를 풀어 볼까 합니다.


콘솔로 또 헬로우 월드를 표시 하는 걸로 이 샘플을 예시 합니다. 일단 콘솔 어플리케이션 프로젝트를 하나 생성 합니다.


그리고 NuGet 패키지 관리자를 실행 시키고 Unity를 찾습니다.


화면으로 보여 드리죵~



요걸 설치 하시면 됩니다. 


코드 봐야 겠죠?



재미있는건 우리가 알고 있는 간단한 IoC 컨테이너는 타입을 넣어 주고 new로 생성된 인스턴스를 넣어서 사용 하고자 할때 타입으로 찾아 꺼내 쓰는 모습 이었는데 이건 좀 다르게 보입니다.


이녀석은 우리가 new를 사용하지 않아도 자신이 new를 해서 반환해 줍니다. 이렇게 생각 하면 좀 이상하게 보이는게 있는데 new를 하는게 언제하냐는 겁니다. 


그래서 아주 간단한 방법으로 테스트 해 보았습니다.


Container.ResisterType 항목을 주석 처리 하고 Resolve에 직접 클래스를 적어주니 객체를 생성해서 반환 합니다.

다음과 같이요..


var test = container.Resolve<Hello>();


와 같이요... 


혹시 기억 하실지 모르겠습니다. Prism 1 에서 MainWindow 를 Resolve 하던 코드를... 거기에선 ResisterType을 하지 않았습니다.


그럼 ResisterType은 무엇일까요? 이건 Dictionary<T, T> 를 사용해 보신 분이라면 아실껍니다. 키를 가지고 실 데이터를 찾게 되는데 그 실 데이터의 타입을 정해 주는 겁니다. Dictionary와 같습니다. 단~ 틀린게 있다면 딕셔너리는 Object 를 등록하게 되지만 여기서는 Type을 등록 하네요~ 즉 정확한 의미는 이겁니다. 만약 인터페이스 타입으로 누군가 Resolve를 요청 하면 이 클래스를 생성해서 반환 하도록 한다.. 라는 사전 지정 입니다.


한번 생각해 보도록 하죠. 만약 유니티 컨테이너를 특정 유틸리티 클래스의 static member로 놓고 거기에 오만 가지 사용하고 있는 클래스를 등록 합니다. 예를 들어 이런 것들이요.. Logger나 File처리 클래스, 통신 클래스 같은 것들이요. 필요할 때 마다 불러서 쓸 수 있겠네요. 프로젝트 전역에서. 참 유용 하겠죠? 


한가지 더 주의 사항이 있습니다. container.RegisterType<IISayHelloWold, Hello2>(); 여기서 interface가 Key 입니다. 


같은 Key를 두번 등록 하면 어떻게 될까요? 바꿔 치기 됩니다. 주의 하시기 바랍니다.


오늘도 수고 하세요~   

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

Prism 1 포스팅에 이어 두번째 포스팅이다. 


우리는 프로젝트 수행 중 몇가지 요구 사항으로 인하여 프로그램 런타임 시에 동적으로 어셈블리를 로딩 하여야 할 경우가 있다. 이럴 때 리플렉션을 이용하여 할 수 있다는 것은 누구나 다 알고 있는 내용일 것이다. 물론 리플렉션은 프로젝트의 타입 안정성을 해친다는 등 단점들이 존재 한다. 리플렉션을 이 포스트에서 논하는 것은 범위 밖 이므로 리플렉션은 MSDN을 참고 하기 바란다.


리플렉션을 자세히 보시려면 이곳을 클릭 -> https://msdn.microsoft.com/ko-kr/library/ms173183.aspx 


원격지 모듈 로딩은 프리즘을 이용하여 원격지에 있는 구성 요소를 로딩 할 수 있는 방법이다.




포스트 1에서 작성 하였던 ModuleA 모듈을 일단 분리 하였다. 이 후 시작 프로젝트에 ModuleCatalog.xaml 을 생성 속성을 다음과 같이 수정 한다. 



코드를 확인 하자.


중요한 것은 xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;

assembly=Microsoft.Practices.Prism.Composition"

Modularity 클래스는 Ref 참조 와 ModuleType 이 필수 항목이다. Ref는 설명하지 않아도 될 듯 하다.

단지 ModuleType 만이 약간의 설명이 필요해 보인다. 


ModuleType의 첫번째 요소는 Type을 표시 한다. 두번째 요소는 Namespace를 의미 하며, 세번째 요소는 Version을 의미 한다. 프로젝트의 AssemblyInfo 클래스를 참조 하자.


이제 이 포스트의 마지막 요소이다. ModulesCatalog.xaml 을 만들었으니 Bootstrapper가 이 xaml 파일을 파싱 로드 해야 할 것이다. 





이상이다. 정말 쉽게 할 수 있다는 것을 알 수 있다. 리플렉션에 비하면 말이다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,



너무 욕심이 과한 건지 모르겠습니다. 사실 엔터프라이즈 라이브러리의 한 파트인 프리즘을 공부 하기엔 제 실력이 너무 허접할 지도 모릅니다. 


하지만... 안할 수 없기에 시작 했습니다. 아무래도 이젠 WinForm 은 버려야 할 듯 합니다. 다음 달이면 Windows 10이 출시 됩니다. 사실 너무 늦게 공부를 시작 하는 듯 합니다. 


국내에선 사실 제대로 된 번역서 한권 없고, 제대로 된 예제 코드 하나 없고, 공부 하기 쉬운 것도 아니고 암튼 그렇습니다. 몇몇 회사에서 프리즘을 사용하여 솔루션을 개발 하고 있다고는 하나 성숙도는 사실 낮습니다. 제가 이런걸 평가 할 수 있는 실력도 아니지만 사실 그렇습니다. 그 만큼 프리즘을 잘 알고 잘 하는 개발자는 드뭅니다. 선구자는 아니어도 세상에 처지지는 말자 라는 것이 저의 작은 소망 입니다만 늘 세상을 따라잡기는 벅차 군요~!!!


일단 프리즘을 다운 로드 받을 수 있는 사이트를 남깁니다. 전 그냥 Nuget으로 다운 받아 버리는 주의라.. 필요 없긴 합니다만... 그래도 프리즘 개발자들과 소통을 할 수는 있습니다.


http://compositewpf.codeplex.com/


프리즘을 설치 하고 제일 먼저 해야 하는 일은 Bootstrapper를 작성해야 합니다. 이 부트스트래퍼가 뭘 하고자 하는 놈이지 알아야 할 겁니다. 


자세한 내용은 MSDN 을 참고 하시길 바랍니다.

https://msdn.microsoft.com/en-us/library/ff921139.aspx


MSDN 가라사대~


"부트 스트랩은 프리즘 라이브러리를 사용하여 구축 된 응용 프로그램의 초기화를 담당하는 클래스입니다. 부트 스트랩을 사용하여 프리즘 라이브러리 구성 요소가 응용 프로그램에까지 연결되는 방법을보다 상세하게 제어 할 수 있습니다." 라고 정의 하고 있습니다. 


위 그림을 보면 첫 단계는 컨테이너 설정 입니다. 뭔가 담길 저장 공간을 설정 하는 군요. 그 이후 레전 맵핑 이라는 것을 합니다. 그리고~~ 대망의 화면을 생성 하는 군요. 그 다음에 각각의 구성 요소들 즉 모듈을 초기화 합니다.


Prism은 부트스트래퍼로 다양한 외부 라이브러리 또는 프리즘에 포함된 MEFBootstrapper등을 사용 할 수도 있습니다만, 저는 프리즘에 포함 되어 있는 UnityBootstrapper를 사용하고자 합니다. 


UnityBootstrapper의 원형을 살펴 보면


UnityBootstrapper는 Bootstrapper를 상속 받았군요. Bootstrapper의 원형도 살펴 보아야 할 듯 합니다.


원형만 바라 보면 이걸 어떻게 하라는 것인지 좀 막연합니다. 이런 코드를 보고 아하~~ 하고 뭔가 눈치를 챌 수 있으면 좋으련만... 전 아직 멀었나 봅니다. 그래도 원형을 살펴 봤으니 억측이라도 해 봐야 겠죠.


원형을 가만히 들여다 보시면 아직 구현 되지 않은 항목이 딱 하나 보입니다. 바로 Bootstrapper 클래스의 CreateShell 메소드가 구현되지 않았습니다. 


이건 확실하게 사용자가 만들어야 하는 클래스가 맞겠군요. 더군다나.. Shell 이라는 건 WPF의 MainPage를 이야기 하는 것일 테니 만들어진 메인 화면을 CreateShell 메소드를 오버라이드 하여 재정의 하도록 하죠.


그런데 말이죠.. 여기 Bootstrapper에 보면 멤버로 IUnityContainer 항목이 있습니다. UnityContainer는 Dependency Injection(의존성 주입) 컨테이너 입니다. 즉 여기에 메인 화면을 넣어 주면 될 듯한 분위기 입니다.


코드 확인 하도록 하죠.


햐~ 어렵습니다. 이게 MainWindow Shell = new MainWindow(); 와 같은 문장이라고 해야 겠죠?
그 다음입니다.. 생성을 했다면 초기화를 해야 겠죠.



일단 이까지가 윈도우를 띄우는 루틴 입니다. 이거 참... 그냥 윈도우 띄우는 것보다 한참 복잡하고 어렵네요 코딩량도 장난 아니구요. 그런데 이걸 왜 쓸까요? 이걸 왜 써야 하는지는 천천히 차근 차근 하다 보면 알아 질껍니다. 명확한 이유는 이거죠. 프리즘은 마이크로소프트의 대표적 의존성 주입 패턴의 결정체 입니다. 


쉘을 생성하고 호출 하여 화면에 띄우기는 했는데... 그 다음은 화면에 뭔가 표시 해야 겠죠. 여기서 나오는 방법이 이제 Region 이라는 것과  Module 이라는 것입니다. 


MSDN 가라사대~~


"모듈은 독립적으로 개발, 테스트 및 (옵션) 배포 할 수있는 기능 패키지입니다. 많은 상황에서는 모듈이 개발되어 다른 팀에 의해 유지됩니다. 전형적인 프리즘 응용 프로그램은 여러 모듈에서 구축되어 있습니다. 모듈은 (예를 들어, 프로파일 관리) 특정 비즈니스 기능을 설명하며, 이러한 기능을 구현하는 데 필요한 모든 뷰, 서비스 및 데이터 모델을 캡슐화하는 데 사용할 수 있습니다. 모듈은 여러 응용 프로그램에서 재사용 할 수있다 (예를 들어, 로깅 및 예외 관리 서비스를위한) 일반적인 응용 프로그램 인프라 나 서비스를 캡슐화하는 데 사용할 수 있습니다."


라고 정의 되어 있습니다. 


Bootstrapper의 원형을 다시 한번 확인 해 보면 대충 눈에 보이는 것이 ConfigureModuleCatalog()라는 원형을 볼 수 있습니다. 반환값은 void 이군요.


일단 Module 클래스를 하나 만들어 보겠습니다. 저 ConfigureModuleCatalog 메소드도 뭔가 넣어야 될 클래스가 필요 할 테니 말이죠.


이렇게 작성을 하고 Bootstrapper의 모듈카탈로그에 등록 하도록 하죠. 추후 Region 맵핑을 하기 위한 키로 "MainRegion" 이라는 스트링으로 등록 합니다.


이 코드를 보시면 ModuleCatalog는 Generic Collection 이라는것 정도는 설명 드리지 않아도 아시겠죠? 

자 이제 까지 하고 보면 참 이상한게 보입니다.코드 어디에서도 ModuleA를 생성 호출 하는 녀석이 없습니다. 이게 바로 Region의 마술 입니다. 일단 우리는 앞에서 MainWindow를 초기화 하고 Show 하였습니다.


그 MainWindow에 xmlns:cal="http://www.codeplex.com/prism" xml namespace를 추가 하도록 합니다. 그리고 Region 맵핑 하도록 하겠습니다. 전체 xaml 코드를 보도록 하죠.


MainRegion을 RegionManager가 모듈 카탈로그를 뒤져서 타입을 찾고 생성하고 초기화 하여 화면에 표시 하는 일을 수행 합니다.

 

정말 어렵습니다. 지금까지 작성한 코드를 보면 단 한군대에서도 명시적으로 new를 이용하여 클래스를 생성하고 사용한 적이 없습니다.


여러분이 감 잡으셨을 지도 모르겠지만 new를 쓰지 않았다는 것은 정말 커다란 의미를 내포 하고 있습니다. 이것은 다시 말해 명시적으로 하위 클래스를 강하게 결합하지 않고 있다는 것 입니다. 결국 하위 클래스는 존재 하여도 그만 존재 하지 않아도 그만 이라는 소리 인데, 이것이 바로 마틴 파울러가 처음 이야기 하였던 제어 역행화(IoC) 이론이며, 어플리케이션을 더욱 견고 하게 만드는 방법 이며...어쩌구 저쩌구.... 


어찌 되었든 프리즘 라이브러리가 어디선가는 new를 이용하여 객체를 생성하고 반환할 것입니다. 그것이 우리 눈에 보이지 않아 정말 어렵게 느껴지고, 개발자가 하지 않은 일을 라이브러리가 순서에 맞춰 대신 해주고 있기 때문에 연결 고리가 쉽게 와 닿지도 않습니다. 정말 어렵습니다. 그러나 멀찍이 서서 조금만 쉽게 바라보면 명확하게 각각의 영역이 분리 되어 있습니다. 개발자는 조금 더 비지니스에 집중 할 수 있도록 만들고 있습니다.


이것이 프리즘의 매력 입니다. 이제 시작 입니다. 해야지 해야지 했던 프리즘 공부를 이제 시작 하려고 합니다. 오늘 부터 말이죠....


장문의 글 읽느라고 고생 많으셨습니다.


그럼 전 이만~~~~~ 


 

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

요즈음 들어 이 DI Pattern에 상당한 관심을 갖게 되었습니다.


이 포스트의 내용은 상당히 주관적인 내용들이기 때문에 제가 잘못 공부 하여 적는 내용들이 있을 지도 모르겠습니다.


잘못된 부분이 있다면 아낌없는 지적을..... 부탁 드립니다.


일단 Dependency Injection 이란게 무엇인지 알아야 겠죠..


전통적인(???) 이라고 해야 할지 모르겠으나, 오래된 프로그래 개발 방법들을 보면 제어를 담당하고 있는 핵심 Core는 해당 프로그램의 모든 Type을 알고 있어야 했습니다.


예를 들자면 아주 오래된 이야기 입니다만 Windows 이전이 DOS 라는 OS를 사용할 당시엔 화면에 무엇인가 출력 하기 위하여 각종 그래픽 카드들의 드라이버를 어플리케이션이 구현 하여, 프로그램에 탑재 해야 했습니다. 더불어 프린터로 출력 하기 위해서는 어플리케이션에서 각종 프린터 드라이버를 탑재 해야 했으며, 음악을 플레이 하기 위해서 각종 사운드 카드 드라이버를 프로그램에 탑재 하여야 했습니다.


그런데 Windows 라는 OS가 발표 되면서 사실 프로그램 개발자는 각종 디바이스 드라이버를 신경 쓰지 않아도 되는 시대가 도래 했죠. 


모든 디바이스 드라이버는 윈도우가 탑재 하였고 그게 어떤 디바이스 드라이버든지 응용 프로그램에서는 신경 쓰지 않고 화면에 출력 또는 프로그램에 출력, 음악 연주를 하는 세상이 Windows를 통해서 도래 하였습니다.


이런 시대가 도래 함으로서 개발자는 상당히 많은 부분의 부담을 덜어도 되었는데 하나의 출력물을 출력 하기 위하여 HP의 프린터인지 Epson의 프린터인지 LG 프린터인지 신경 쓰지 않아도 되는 정말 편리한 세상이 된 것이죠. 


이런 형태를 하나의 Dependency Injection(의존성 주입) 패턴이라고 봐도 무방 합니다.


즉 어플리케이션은 OS에 어떤 프린터가 설치 되어 있는지 몰라도 되는 상황이 된 것이죠 결국 출력 하기 위해서는 Print 라는 명령문만 날려주면 출력이 이루어지는 세상 말이죠. 


이 DI Pattern 을 구현하고 있는 프레임웍들이 상당히 많은데 사실 이런 용어를 제일 먼저 이야기 한 분은 이 시대의 개발 선구자 중 한명인 마틴 파울러 라는 분입니다.


마틴 파울러가 Inverse of Controls(IoC) 제어 역행화 방법론을 제일 먼저 소개 하였으며, 이것이 많은 개발자들에게 영감을 주어 정말 많은 프레임웍이 세상에 등장 하게 되었죠. 


많은 사람들에게 DI 와 IoC가 같은 레벨의 방법론으로 인지 되고 있으나 사실 DI에 비하여 IoC가 조금 더 추상적인 내용 입니다.


DI 패턴을 공부 하기 위하여, 우리가 제일 먼저 알고 있어야 하는 것은 OOP의 Interface 입니다.


눈치 빠른 분들께선 뭐 당연한 이야기네 라고 말씀 하시겠지만. Interface라는 것이 어떤 타입이든 관계 없이 해당 타입의 메소드 만 존재 한다면 타입 캐스팅이 필요 없이 사용할 수 있는 메커니즘 때문 이라는건 누구나 주지 하고 있는 사실 일 것입니다.


뭐 이쯤해서 서론은 마치기로 하고 몇가지 패턴들에 대해서 이야기 해 보도록 하겠습니다.


GoF의 디자인 패턴 중 Factory 패턴을 보게 되면 이 패턴이 이야기 하고 있는 것은 여러개의 동일한 행위를 하는 여러 타입의 객체를 중앙 집중화 할 수 있는 방법입니다.


회사의 규모에 따라 하나의 회사에는 여러명의 사원이 존재 합니다.


여러명의 사원은 각기 다른 업무를 수행하게 되겠죠.. 영업, 개발, 연구, 경원지원 등등등..... 과 같이 말이죠.


만약 한명의 개인의 급여를 계산 하는 프로그램이 있다고 가정 한다면 급여를 계산 하는 메인 프로세서는 이 사람의 부서, 직책, 직위, 시간외 업무 수당, 실적, 보너스 .. 등등 많은 것들을 고려 하고 있어야 할 것입니다. 


급여 계산을 수행 하고자 하는 메인 프로그램이 모든 사원에게 기본급을 계산 하는 룰은 같다고 가정 하고 각각의 부서별로 다른 급여 계산을 하는 비지니스 모델이라고 한다면, Employee 라는 추상 클래스로서 기본급을 계산 하는 Type을 생성 하고 이 Employee 라는 클래스를 상속 받는 부서별 Class를 작성하여 비지니스 로직을 분리 하고 이 객체를 좀 더 쉽게 생성 중앙 집중화 할 수 있도록 하는 Factory Pattern 을 사용하게 될 것입니다.


컨트롤러의 입장에서 바라 본다면 이 패턴의 문제점은 다음과 같을 것입니다.

어찌 되었든 컨트롤러 입장에서 바라 볼 때, 컨트롤러는 급여 계산을 하기 위하여 어떤 부서가 있는지 명시적 타입을 알고 있어야 할 것입니다.


이것은 DI 패턴이라고 할 수 없죠. 그래서 제시된 방법론이 ServiceLocator 라는 패턴 입니다.


이 ServiceLocator의 핵심은 다음과 같은 코드 입니다.


public class ServiceLocator
{

    private IDictionary<string, object> registeredTypes = new Dictionary<string, object>();

    public void Register<T>(string ServiceName, T obj)
    { 
        registeredTypes.Add(ServiceName, obj);
    }

    public T Locate<T>(string ServiceName)
    {
        return (T)registeredTypes[ServiceName];
    }
}

이 코드를 보면 Dictionary의 Key를 string 타입으로 지정 한 것을 볼 수 있습니다. 즉 각각의 부서명을 이용하여 실제 Object를 취득 하게 됨을 알 수 있습니다.


결국 메인 컨트롤러는 명시적 타입을 알지 못해도 되는 효과를 얻기는 했으나.....


두번째 정말 커다란 문제가 있죠.


만약 회사가 발전하게 되고 회사의 부서가 점점 늘어 나게 될 경우 메인 컨트롤러는 각각의 부서를 Register 하기 위한 코드가 부담 스러워 지게 될 것입니다.


역시 이 또한 Factory 패턴과 별반 다를게 없어 보입니다.


이것을 개선 하기 위하여 작성된 IoC 컨테이너의 핵심 코드를 보도록 하겠습니다.



public class IoCContainer
{

    private IDictionary<Type, object> registeredTypes = new Dictionary<Type, object>();

    public void ADD<T>(T instance)
    { 
        registeredTypes.Add(typeof(T), instance);
    }

    public T GetInstance<T>()
    {
        return (T)registeredTypes[typeof(T)];
    }
}

ServiceLocator 패턴의 핵심 코드와 별반 다르지 않습니다.


그런데 말이죠... 이 코드의 유용함이란.. 정말 이루 말 할 수 없겠네요.  이것으로 DI Container 의 기초 정리는 마무리....... 합니다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

싱글톤 패턴의 의미는 프로젝트 전역에 걸쳐 객체를 단 하나만 생성하겠다는 의미 입니다.


예전 모 프로젝트 진행 도중 어떤 선임 개발자가 이 Singleton 패턴을 남발 하여 아주 곤란한 상황에 직면했던 적이 있는 그런 모델이기는 하나 시기 적절 하게 사용하게 되면 정말 유용한 패턴 입니다.


일단 이 Singleton Pattern을 이해하기 위하여 이 패턴의 용도를 알아야 겠습니다.


싱글톤 패턴의 용도는 쉽게 이야기 한다면  "공유 데이터의 동기화"에 해당하는 문제를 풀어 나가는 방법 입니다.


다시 말하여. 

A와 B라는 객체가 동일한 temp라는 프로퍼티를 가지고 있다고 할 때, 둘의 데이타는 언제나 같은 값으로 동기화 되어야 한다면, A.temp가 변경 될 때 B.temp도 변경 하여야 하고 B.temp가 변경 될 때 A.temp도 변경 되어야 한다는 조건이 성립합니다.


이것은 프로그래밍을 할 때 피해야 하는 요소 중 한가지 순환 참조의 조건에 해당 합니다.


당연히 순환 참조가 성립 되면, 객체는 각각 객체가 가진 프로퍼티에 대입되는 값을 동기화 하기 위하여 무한 루프에 빠지게 될 것이며, 이것을 해결 하기 위하여 우리는 또 인다이렉션이라는 설계를 하게 되겠죠


역시 모든 소프트웨어 공학의 정답은 인다이렉션 입니다.


리팩토링을 설명 하면서 잠깐 데이터와 상태의 전달에 대하여 언급한 적이 있는데. 역시나 같은 문제를 놓고 고민 하게 되는 것입니다.


싱글톤 패턴의 또 다른 용도로 DB 컨넥션에 해당하는 예를 들 수 있습니다. DB는 하나의 컨넥션이 맺어질 때마다 메모리를 할당 하게 되고 하나의 어플리케이션이 다수의 컨넥션을 맺게 되면 하나의 PC는 더 많은 DB 컨넥션을 맺게 될겻이고, 이렇게 되면 대규모 사이트 일 수록 DB서버는 어마 어마한 부하를 받게 될 겁니다.


위와 같은 문제로 3 Tier를 구성 하겠지만, 2 Tier의 상황에서도 하나의 어플리케이션이 단 하나의 컨넥션만을 유지 할 수 있다면 DB쪽 부하를 상당히 줄여 줄 수 있을 것입니다. 물론 미들티어를 가지고 있는 3 Tier의 상황에서도 클라이언트가 컨넥션을 한개만 유지해 준다는 것은 미들티어의 부하를 줄여 주는 방법이기도 합니다. 이렇게 보든 저렇게 보든 부하를 줄이기 위하여 다수의 컨넥션이라는 문제를 방치하는 것은 상당히 위험해 보입니다.


이런 문제로 하나의 컨넥션을 유지하는 명시적인 방법으로 싱글톤 패턴을 활용 하기도 합니다.


물론 비지니스 성격에 따라 하나의 어플리케이션은 다수의 컨넥션을 요구 하기도 합니다. 이런 문제를 잘 파악하여, 싱글톤 패턴을 활용 할 수 있다면 비교적 좋은 성능을 가질 수 있을 것입니다. 어떻게 보면 정답 처럼 보이기도 합니다.


이 컨넥션 문제를 놓고 또 한가지 고민 해야 할 사항이 있습니다.


하나의 어플리케이션은 OS로 부터 메모리를 할당 받으면서 임계 영역이라는 것으로 메모리 보호를 받게 됩니다. DLL 제작과 같은 방법으로 Data Access Layer를 Singleton Pattern으로 구성한다고 하여도 이 임계 영역이라는 문제로 인하여 하나의 어플리케이션 과 다른 어플리케이션은 객체를 공유 할 수 없는 문제가 발생 합니다. 예전 이런 문제를 해결 하기 위하여 DCOM과 같은 기술들이 나온 것이기도 합니다.


이것의 의미는 아무리 DAL을 잘 구성 한다고 하여도 각각의 어플리케이션마다 컨넥션을 맺게 된다는 것이고 하나의 PC는 다수의 컨넥션을 맺게 됩니다. 이유는 간단 합니다. 메모리 보호 문제로 각각의 어플리케이션이 가지고 있는 메모리 어드레스를 두 어플리케이션이 공유 할 수 없기 때문입니다.


.Net 환경에서 이것을 좀 쉽게 피해 갈 수 있는 방법이 있습니다. Component Model을 활용하면 쉽게 해결 할 수 있습니다. DCOM의 확장판은 Component Model이기 때문입니다.


이쯤 적으면 어느정도 초보 단계를 벗어난 개발자분이시라면.... 아 Remoting 또는 WCF를 활용하면 되겠구나 하실 껍니다. 맞습니다. Remoting, WCF 등등이 답입니다.


참고 MSDN : http://msdn.microsoft.com/ko-kr/library/vstudio/72x4h507(v=vs.100).aspx


을 확인 하시면 Remoting 은 호환성을 유지하기 위하여 아직 사용되고 있으나, 새로운 개발에는 WCF를 사용할 것을 권장 하고 있습니다. 


싱글톤 패턴을 설명 하려고 하다가 너무 멀리 와 버렸네요.


이 처럼 객체를 하나만 사용하겠다는 것은 많은 것들을 이야기 합니다. 또 다른 관점으로는 메모리와 데이타를 어떻게 사용하느냐의 문제 이기 때문에 프로그래밍의 발전 역사와도 맞물려 돌아 가겠죠.


어플리케이션이 동작하는 상태를 만약 싱글톤 패턴으로 관리 하게 된다면 어떨 까요? 현 상황에서 어떤 로직으로 분기 해야 하는지 아니면 현 상황에서 어떤 화면을 나타내야 하는지 등에 해당하는 내용을 싱글톤 패턴으로 관리 하게 된다면 좀 쉽지 않을까요? 여기 저기서 상태를 체크 하는 .. 또는 정리 되지 않은 또는 여기 저기 산재 되어 있는 비지니스 프로세스 때문에 고민하는 우는 좀 줄일 수 있지 않을까요? 거의 완벽에 가까운 인다이렉션 설계로 종속성 관리를 완벽하게 하지 못한다면 말이죠...


다음과 같은 C#  코드로 싱글톤 패턴을 구현 할 수 있습니다. 차암 쉽죠잉~~~ ^^

class SingletonTest
    {
        static void Main(string[] args)
        {
            var singleton = Singleton.getObject();
            getObject.SaySomething();

            Console.ReadKey();
        }
    }

    public class Singleton
    {
        private static Singleton _SingleObject;

        private Singleton() { }

        public static Singleton getObject()
        {
            if (_SingleObject== null)
            {
                _SingleObject= new Singleton();
            }
            return _SingleObject;
        }

        public void SaySomething()
        {
            Console.WriteLine("Test Singleton");
        }
    }

여기서 OOP를 잘 이해 하지 못하는 분들을 위해 몇가지 부가 설명을 적습니다.


규칙

1. 싱글톤 오브젝트는 오로지 하나여야 하기 때문에 static으로 제한 합니다.

2. 인스턴스는 static에 접근 할 수 있으나 static은 인스턴스에 접근 할 수 없습니다.


위 두가지 규칙이 Singleton패턴의 모든 규칙이라고 할 수 있습니다. 그런데 한가지 좀 심하게 고민 해 봐야 할 문제가 있습니다. 우연히도 완벽하게 동일한 시스템 클럭에 완벽하게 동일하게 두개의 클라이언트가 동시에 싱글톤 객체를 생성하려고 한다면 어떻게 하여야 할까요? 한번 고민해 보시길 바랍니다.


Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,


Head First - Design Pattern 에 정의 하길....

Strategy Pattern에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트레티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경 할 수 있다.


위 정의만을 놓고 보자면 도대체 이게 무슨 소리 인지 이해하기가 쉽지 않네요.


일단 스트레티지 패턴을 이해 하기 위하여, 몇 가지 사전 지식이 필요합니다.


1. 추상클래스

2. 인터페이스

3. 상속

4. 업.다운캐스팅


수 많은 프로그래밍 구루들이 이야기 하길 사실 프로그래밍을 하기 위하여 꼭 알아야 하는 것으로 상속과 추상클래스, 인터페이스 보다 중요한 것은 없다고 이야기들을 합니다.


스트래티지 패턴은 위에서 언급한 4가지 개념으로 만들어 집니다. 만일 위 4가지 개념중 한가지라도 좀 미흡하다 싶은 분께서는 먼저 저 4가지 개념 부터 다시 학습 하신다음에 스트레티지 패턴을 보시면 이해 하기가 훨씬 용이할 겁니다.


이제 스트레티지 패턴을 풀어 보도록 하죠


한국 사람이라면 누구나 수저를 이용하여 밥을 먹을 껍니다. 그런데 사실 우리 와이프는 저보다 젓가락질을 잘 못합니다.


이게 바로 스트레티지 패턴의 전형적인 예 입니다.


어떤 사람은 밥을 먹을때 포크와 숟가락 으로만 먹을 것이며, 어떤 사람은 숟가락으로만 ... 어떤 사람은 젓가락으로만... 뭐 이렇게  밥을 먹을때도 각각의 사람들은 각각의 자기만의 밥 먹는 방법이 있을 겁니다. 그냥 이렇게만 한정 지어 버린다면 이 문제를 상속과 추상클래스 만으로 풀어 내려고 해도 됩니다. 아주 훌륭한 방법이 되겠죠.


그런데 어느날 갑자기 우리 와이프가 젓가락질을 겁내 열심히 연습해서 저보다 젓가락질을 잘 하게 될 수도 있을 겁니다. 그럼 행동 패턴이 바뀌게 됩니다. 지금은 젓가락질을 잘 못하기 때문에 숟가락으로만 밥을 먹다가... 어느날 갑자기 수저로 밥을 먹게 되어 버린거죠. 행위의 속성이 한가지 추가 되어 버린겁니다. 


이렇게 되어 버리면 상속이나 추상으로만 놓고 보면 추 후 유지보수측면에서만 바라 보더라도 좀 곤란한 상황이 발생 하게 될 수도 있습니다. 


좀 더 확장해서 생각해 보면 상속이나 추상으로만 해결 하였을 경우 우리 와이프가 젓가락질을 잘 못한다고 우리 와이프의 뱃속에서 태어나 자란 아이가 젓가락질을 잘 못할 수도 있겠지만... 저를 닮아서 잘 할 수도 있다는 점이죠... 


결국 행위에 대한 정의는 쉽게 변경 될 수 있으나 상속을 통해서 행위에 대한 정의를 변형 하려고 하면 부모로 부터 상속 받은 문제등으로 인하여 곤란한 상황이 될 수 있을 거라는 뜻 입니다. 


이런 문제를 해결 하기 위한 것이 인터페이스이며, 이 인터페이스를 활용하여 우리의 프로그래밍 구루 몇분께서 스트레티지 패턴을 만들어 놓으셨습니다.


스트레티지 패턴을 구현하기에 앞서 우리가 먼저 외워야 할 것들이 있습니다.


1. Application에서 달라지는 부분(행위)과 달라지지 않는 부분(몸체)을 구분하여 분리 하여야 한다.

2. 상위 형식에 맞추어 프로그래밍 하라.(업캐스팅) - A=B 이다가 아니라. A에는 B가 있다는 것이 훨씬 좋을 수 있으므로 인터페이스를 선언하고 상위 형식으로 인터페이스를 이용하는 방법 이 좋을 수 있다는 뜻 입니다.



using System;

namespace Strategy
{
    public class MiniDuckSimulator
    {
        static void Main(string[] args)
        {
            Duck mallard = new MallardDuck();
            mallard.Display();
            mallard.PerformQuack();
            mallard.PerformFly();

            Console.WriteLine("");

            Duck model = new ModelDuck();
            model.Display();
            model.PerformFly();

            //런타임에 날 수 없는 장난감 오리를 겁내 빨리 날도록 바꿨어요.
            model.FlyBehavior = new FlyRocketPowered();
            model.PerformFly();

            Console.ReadKey();
        }
    }

    #region Duck

    public abstract class Duck
    {
        public IFlyBehavior FlyBehavior { get; set; }
        public IQuackBehavior QuackBehavior { get; set; }

        public abstract void Display();

        public void PerformFly()
        {
            // A에는 B가 있다.
            FlyBehavior.Fly();
        }

        public void PerformQuack()
        {
            QuackBehavior.Quack();
        }

        public void Swim()
        {
            Console.WriteLine("모든 오리는 물위에 뜰 수 있어요.");
        }

    }

    public class MallardDuck : Duck
    {
        public MallardDuck()
        {
            //인터페이스 업캐스팅
            QuackBehavior = new LoudQuack();
            FlyBehavior = new FlyWithWings();
        }

        override public void Display()
        {
            Console.WriteLine("나는 청둥오리예요");
        }
    }

    public class ModelDuck : Duck
    {
        public ModelDuck()
        {
            // 인터페이스 업캐스팅
            QuackBehavior = new LoudQuack();
            FlyBehavior = new FlyNoWay();
        }

        override public void Display()
        {
            Console.WriteLine("나는 실제 오리가 아니라 장난감 오리예요.");
        }
    }

    #endregion

    #region FlyBehavior

    public interface IFlyBehavior
    {
        void Fly();
    }

    public class FlyWithWings : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 날아 다녀요.");
        }
    }
    public class FlyNoWay : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 날지 못해요!");
        }
    }
    public class FlyRocketPowered : IFlyBehavior
    {
        public void Fly()
        {
            Console.WriteLine("나는 겁내 빨리 날아다녀요!");
        }
    }
    #endregion

    #region QuackBehavior

    public interface IQuackBehavior
    {
        void Quack();
    }

    public class LoudQuack : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("크게 소리내요");
        }
    }

    public class MuteQuack : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("나는 조용한 오리라구요.");
        }
    }

    public class Squeak : IQuackBehavior
    {
        public void Quack()
        {
            Console.WriteLine("작게 소리내요.");
        }
    }
    #endregion
}



Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,