이전 포스트 Dependency Inversion Principle 에서 의존하는 모듈, 레이어 사이에 추상 (interface 또는 abstract class)을 의존함으로써 구현의 상세(detail)를 의존할 때보다 느슨한 커플링을 만들 수 있고,  구조적 설계와 비교할 때 의존의 방향이 역전 되었다라고 하며, 의존의 전이를 끊었다라고 말했습니다.

Inversion of Control 은 프레임워크가 정의한 추상(interface 또는 abstract class)을 클라이언트 코드가 구현을 하고, 구현된 객체를 프레임워크에 전달(또는 주입) , 프레임워크가 제어를 가지게 함으로써 클라이언트 코드로 부터 제어의 수를 줄이게 하는 것이라고 말할 수 있습니다. 모든 제어를 클라이언트 코드가 가지고 있는 구조적 설계와 비교해 프레임워크가 제어를 가지는 것을 제어가 역전 되었다 라고 말합니다.

Invsersion of Control Container 란 무엇일까요? Invsersion of Control 을 담고 있는 상자 일까요?



용어의 개념을 왜곡하지 않고 가능한 원래의 의미를 알기 위해 마틴 파울러의 [ Inversion of Control Containers and the Dependency Injection pattern] 의 글을 번역 인용하여 IoC Container를 설명하고자 합니다.

콜론(:) 구분자로 저장된 영화의 목록에서 특정 감독의 영화를 조회하는 예제는 아래와 같습니다.

 public class MovieLister
 {
        private MovieFinder finder;
        public MovieLister()
        {
            finder = new ColonDelimitedMovieFinder("Movies1.txt");
        }
 
        public List MoviesDirectedBy(String arg)
        {
            List items = new List();
            List allMovies = finder.FindAll();
             
            foreach (Movie item in allMovies)
            {
                if(string.Equals(item.DirectorName, arg)){
                    items.Add(item);
                }
            }
 
            return items;
        }
    }
 
    public interface MovieFinder
    {
        List FindAll();
    }
 
    public class ColonDelimitedMovieFinder:MovieFinder
    {
        string sourceFilename = string.Empty;
 
        public ColonDelimitedMovieFinder(string arg)
        {
            this.sourceFilename = arg;
        }
 
        #region MovieFinder 멤버
 
        public List FindAll()
        {
            List items = new List();
 
            //Read From file : sourceFilename;
            //Split with Colon Delemiter
            //Add to items List
            string rawText = File.ReadAllText(sourceFilename);
            foreach (string row in rawText.Split(new char[]{':'}))
            {
                items.Add(new Movie(row.Split(new char[] { '|' })[0],

 row.Split(new char[] { '|' })[1])); 
            }
            return items;
        }
 
        #endregion
    }
 
    public class Movie
    {
        public string Name;
        public string DirectorName;
        public Movie(string name, string directorName)
        {
            this.Name = name;
            this.DirectorName = directorName;
        }
    }
}
 


이 클래스를 나만 사용한다면 깔금하고 멋져보입니다. 하지만, 나의 친구가 이 멋진 기능을 자신의 프로그램에 복사해서 넣고 싶다면 어떤 일이 벌어질까요? 그들이 영화 목록을 콜론으로 구분된 "movie1.txt"라는 이름의 텍스트파일로 정한다면 환상적일  것입니다. 다른 파일이름을 사용하고 싶다해도 속성값 만 변경하면 되니 괜찮습니다. 하지만 영화목록을 위한 완전히 다른 폼을 사용하길 원한다면 : SQL 데이터베이스, XML 파일, 다른 텍스트파일 포멧 , 이 경우에 데이터를 가져오는 다른 클래스가 필요 합니다. 나는 MovieFinder interface를 사용했기 때문에 MoviesDirectedBy 메소드는 변경하지 않아도 됩니다. 하지만 알맞는 finder 인터페이스 구현 인스턴스를 얻을 방법이 필요합니다.


이 상황을 P of EAA 에서는 Plugin 이라고 기술합니다. finder를 위한 구현 클래스는 컴파일타임에 링크되지 않습니다. 내 친구가 어떤 구현을 원할지 모르니 Lister 가 특정 구현과 일하는 대신, 내 손을 떠나 나중에 플러그 될 수 있도록 합니다. 문제는 어떻게 해야 Lister가 구현 클래스를 무시하면서도 여전히 인스턴스와 일할 수 있게 하느냐 입니다.

이것을 실제 시스템으로 확장하자면, 우리는 수십개의 이런 서비스와 컴포넌트를 가지고 있을 것입니다. 이런 경우에 interface 를 통해 이야기하는 방법으로 컴포넌트 사용 방법을 추상화 할 수 있습니다. ( 컴포넌트가 interface를 가지도록 설계되지 않았다면 Adapter를 사용할 수 있습니다.) 


중요한 문제는 이 플러그인들을 어떻게 어플리케이션에 조립하느냐 입니다. 이것이 경량 컨테이너 lightweight Container 가 부상하게 이유입니다. 일반적으로 Inversion of Control을 이용해 해결할 수 있습니다.
 


주: 여기서 경량 컨테이너란 Service Locator, PicoContainer, Spring, Avalon, Guice 등을 말합니다.

주: 여기서 Inversion of Control을 이용해 해결한다는 말은 구현을 선택하는 전략을 가진 객체에게 제어를 넘김으로써 구현을 찾도록 한다는 것을 의미합니다.

주 : 추상을 사용함으로써 커플링을 느슨하게 하는 아이디어는 로버트 C. 마틴의 Dependency Inversion Principle 이 도입된 것입니다.



Inversion of Control 

이 컨테이너들이 "Inversion of Control"을 구현하고 있기 때문에 유용하다고 말할 때 완전히 혼란 스러워졌습니다. Inversion of Control 은 프레임워크의 일반적인 특성입니다. 따라서 경량 컨테이너들이 특별하다라고 말하는 것은, 내 차는 바퀴를 가지고 있기 때문에 특별하다라고 말하는 것입니다.

그렇다면 과연 컨트롤의 어떤 관점이 역전 되었다는 것일까? 내가 처음 제어의 역전을 말했을 때는 사용자 인터페이스의 제어를 의미하는 것이었습니다. 초기의 사용자 인터페이스는 어플리케이션에 의해 제어 되었습니다. 여러분은 "Enter Name" , "Enter Address" 와 같은 순차적인 명령을 가지고 있고, 여러분의 프로그램은 프롬프트를 나타낸 후 각각에 대해 응답을 선택할 것입니다. main loop를 담고 있는 그래피컬 UI 프레임워크를 가지고 작업한다면, 스크린의 다양한 필드를 위한 이벤트 핸들러가 제공되고, 프로그램의 중앙 제어는 UI 프레임워크에게 역전됩니다. 제어는 여러분에게서 프레임워크로 이동한 것이죠.


컨테이너에게 있어서 역전 이라고 하는 것은 컨테이너가 플러그인 구현을 스스로 찾는 것을 의미 합니다. 나의 순진한 Lister는직접 인스턴스화된 finder 구현을 찾습니다. 이렇게 하면 플러그인으로 사용할 수 없게 됩니다. 컨테이너를 사용하면 Lister에 구현을 주입할 수 있습니다. 


심사숙고한 후 우리는 이 패턴을 위한 좀더 구체적인 이름이 필요하다는 결론에 도달했습니다.  제어의 역전 Inversion of Control은 너무 일반적인 용어여서 사람들이 매우 혼란스러어 했습니다. IoC 에 대한 다양한 토론을 거친 결과 우리는 IoC를 Dependency Injection 이라고 이름 붙이기로 했습니다.


마틴 파울러의 [ Inversion of Control Containers and the Dependency Injection pattern] 중 일부 


no more IoC , it`s Dependency Injection!



위의 내용을 요약하자면 :
 

이미 작성된 코드를 다른 어플리케이션에 재사용하거나, 의존하고 있는 객체의 행위를 컴파일타임 이후에 다른 행위로 플러그 하고자 한다면, 클라이언트 객체는 추상을 가지고 작업을 해야 합니다. 이 추상에 구현을 교체할 수 있는 패턴을 마틴파울러는 Plugin 패턴이라고 이름 붙였습니다. 그럼 추상을 구현한 다양한 플러그인 중에 알맞는 플러그인을 선택해야하는 문제가 남게 됩니다.

의존 그래프의 생성과 생성된 의존을 주입 하는 일이 코드의 여러곳에 반복적으로 난립하게 되는 문제이지요, 의존 그래프의 생성과 주입 전략을 가진 객체가 등장 했으니 이것을 경량 컨테이너 라고 하는 놈 입니다. 제어를 넘겨 받은 컨테이너 이므로 Inversion of Control Container 라는 이름을 얻게 되었습니다. 그런데, IoC Container 라는 이름이 너무 일반적인 용어라서 사람들이 너무 혼란스러워 하게 되었고, IoC Container 가 하는 일이 결국에는 의존을 주입하는 일이기 때문에 Dependency Injection 이라 부르기로 결정했습니다.




그럼 의존을 주입하는 방법이 궁금해 질 것입니다. 의존을 주입하는 방법으로는 
1. Constructor Injection
2. Setter Injection
3. Interface Injection 
의 크게 세가지가 있으며, 주입할 객체를 생성하는 설정을 코드에 내재하거나 설정파일(주로 XML 파일) 을 이용하기도 합니다.


이 포스트는 IoC Container 의 개념을 설명하는 목적으로 여기에서 줄이며, 주입하는 방법까지 설명은 마틴 파울러의 
Inversion of Control Containers and Dependency Injection pattern 글과 이 글을 번역 설명한 행복한 아빠 님의  IoC 와 DI 에 대한 블로그 포스트 시리즈 1, 2, 3 포스트를 읽어보시기 바랍니다.

토비님의 블로그와 저서 스프링 프레임워크 3.0 은 IoC  컨테이너의 자세한 내용을 담고 있습니다. 

마틴 파울러의 [Inversion of Control Containers and the Dependency Injection pattern] 글에 대한 여러 개발자님들의 번역이 있군요. 그 중 SKY-TIGER 님의 번역 글을 링크 합니다. 





참고 :
Inversion of Control Containers and Dependency Injection pattern by 마틴 파울

저작자 표시
신고

이 글을 Twitter / Facebook 에 공유하기
이 글이 유익하다면 아래의 트위터 버튼을 눌러 공유해 주시거나, 페이스북 "좋아요" 버튼을 눌러 주세요.

   


Posted by 반더빌트
소프트웨어 개발과 패턴은 어떤 관계가 있을까요? 초급의 개발에서는 패턴에 대한 필요성 자체를 인지하지 못합니다. 딴 세상 이야기 처럼 말이죠. 개발자들도 중요성 인지가 안되어 있으니 심지어는 '디자인 패턴 design patterns' 책이 그림 코너의 서가에 꽂혀 있던 때도 있었습니다. 책 이름이 '디자인' 이란 이유로 말이죠.  재사용 가능하며 유지관리 가능한 소프트웨어어 대해 고민하기 시작하면, 그때서야 웹어플리케이션, 클라이언트 어플리케이션, 서버 어플리케이션 영역에 관계없이 객체지향디자인과 패턴이 필요하다는 것을 인식하게 됩니다.

소프트웨어 개발은 개발 환경과 구동 환경을 이해하는 것으로 부터 시작합니다. 통합개발환경 IDE Integrated Development Environment 에서 개발하는 것이 가장 쉬운 방법입니다. 닷넷개발자라면 Visual Studio, 자바개발자라면 Eclipse 같은 환경 말이죠. 개발도중 또는 개발된 소프트웨어는 구동될 수 있는 호스트 Host 라는 환경이 필요한데, IDE의 장점은 자체적으로 호스트를 지원해서 구동 환경에 대해 걱정할 필요가 없습니다.

비개발자가 개발이라고 부르는 코딩이라는 작업을 하려면 '언어'라는 무기가 필요합니다.  언어는 칼과 같죠. 요리를 할때는 식칼을, 결투를 할 때는 긴 칼을 사용하 듯이 웹페이지의 화면을 구성할 때는 HTML을, 상호작용을 위해서는 자바스크립트 언어를, 내부 콘트롤을 위해서는 C#등의 언어를 사용합니다. 

언어를 사용하려면 언어의 능력과 특성을 알아야 겠죠. 바로 언어의 사양 specification 입니다. 글 과 문법을 알았다면 이제 시를 써야 겠지요.

프랙탈 : 혼돈 속의 규칙적인 패턴이 자연계 및 생명체를 만들어 내는 것이 소프트웨어의 패턴과 닮았다.



소프트웨어 개발의 목적은 어떠한 문제를 해결하는 것 입니다. 원래의 의도대로 정확하게 구동 되는 것이 가장 첫번째 달성해야 하는 목표라고 할 수 있습니다. 허나 소프트웨어는 구동되는 것 만이 전부가 아닙니다. 잘 개발된 객체지향 소프트웨어가 가져야 할 특성은 구획화 된 모듈성 modularity, 변경을 반영할 수 있는 유연성 flexibility, 요구 추가를 위한 확장성 extensibility, 이 요소들을 갖추고 있으면서도 소프트웨어가 생명주기동안 살아 있을 수 있도록 하는 유지관리성 maintainability 입니다.

언급한 특성들을 갖추게하는 증명된 방법 그 것이 바로 '패턴 Pattern' 입니다.


소프트웨어를 개발하다 보니 구현 코드는 달라도 유사한 문제가 발생하는데, 그 때 패턴을 사용하게 되고, 개발 관련자들 사이에 의사소통 도구로 사용됩니다. 또한 잘 구성된 패턴은 해결하려는 문제가 무엇인지 유추 가능케 합니다.


패턴이라는 것은 어떻게 공부 할 수 있는가?

패턴에는 레벨이라는 것이 존재 합니다.  코드, 클래스, 아키텍처 레벨로 구분할 수 있습니다.

코드 단위의 패턴을 공부할 수 있는 가장 좋은 책은 [켄트 벡의 구현 패턴] 입니다. 코드 단위의 문제해결에는 수많은 방법이 존재합니다. 코드는 컴퓨터, 자신 그리고 동료와의 의사소통 입니다. 정확하게 구동되어야 하고, 군더더기 없이 의도를 밝히는 코드가 좋은코드의 조건이죠. 개발자는 한땀 한땀 작성 할 때 마다 수많은 고민과 선택을 합니다. 좋은 코드의 조건을 만족시키기 위해 말이죠. 켄트 벡의 책은 코드 구현 단위에서의 고민을 덜어 줄 수 있는 패턴을 이 책에 담고 있습니다.

구현 코드를 감싸는 클래스 단위에서 그 유명한 디자인 패턴 Design Pattern 이 등장합니다. 재사용 가능한 소프트웨어 개발에 대한 고민으로 시작된 객체간의 관계 패턴으로 구조 Structural, 생성 Creational, 행위 Behavioral 의 세가지로 구분되어 있습니다. 디자인 패턴을 공부할 수 있는 책들은 다수가 존재하는데 우선 바이블 [GoF의 디자인 패턴 ] 을 빼놓을 수 없습니다. 디자인 패턴은 구현패턴에 비해 어렵습니다. 여러권의 책을 읽어야죠.  마틴 파울러의 [리팩토링]은 구현패턴과 디자인패턴을 커버합니다. 특히 구조적코드 Structural code가 객체적 코드, 디자인으로 어떻게 변경되어야 하는지 체감 수준의 책입니다. 

세번째 아키텍처 패턴입니다. 패키지, 모듈, 레이어 수준의 패턴을 의미합니다. 좋은 책으로는  마틴 파울러 [PoEAA (Patterns Of Enterprise Application Architecture)] 와 좀더 추상화된 설명으로 에릭 에반스의 [도메인-드리븐 디자인]이 있습니다. 여기서 언급되는 것들은 레이어링, 도메인 로직, 데이터베이스 매핑, 어그리게이트, 리파지터리의 개념들 입니다.



코드 단위 
켄트 벡의 구현 패턴, 켄트 벡저/전동환역, ISBN-10: 108960770310

클래스 단위
GoF의 디자인 패턴 : Elements of Reusable Object-Oriented Software, 에릭 감마,리처드 헬름,랄프 존슨,존 블라시디스 공저, ISBN-10:8945072144
Refactoring 리팩토링 : 나쁜 디자인의 코드를 좋은 디자인으로 바꾸는 방법, 마틴 파울러 저/윤성준,조재박 공역, ISBN-10:898793960X


패키지, 모듈, 레이어 단위
Patterns of Enterprise Application Architecture, Martin Fowler, ISBN-10: 0321127420
Domain-Driven Design : Tackling Complexity in the Heart of Software, Eric Evans, ISBN-10: 0321125215


이 블로그의 DDD & PoEAA 카테고리는 아키텍처 수준의 개념과 구현에 대한 글들의 모음 입니다.

### 끝.
저작자 표시
신고

이 글을 Twitter / Facebook 에 공유하기
이 글이 유익하다면 아래의 트위터 버튼을 눌러 공유해 주시거나, 페이스북 "좋아요" 버튼을 눌러 주세요.

   


Posted by 반더빌트


마틴 파울러 PoEAA(패턴 오브 엔터프라이즈 어플리케이션 아키텍처) 요약.

마틴 파울러의 PoEAA는 엔터프라이즈 어플리케이션의 아키텍처가 어떻게 구성되는 것인가에 대한 최고의 책입니다. 도입부의 [도메인 로직을 구성하는 패턴]은 조금은 어렵게 느껴지는 내용입니다. 전체적인 내용을 파악한 후 몇번에 걸쳐 정독 하는 것이 이 책의 내용을 이해하는 데 도움이 될 것입니다.

아래는 PoEAA 의 전체적인 요약 포스트의 번역 입니다.

엔터프라이즈 하면 역시 스타트랙.PoEAA




원문주소 : http://moshfegh.spaces.live.com/blog/cns!40F968A11C62F49A!192.entry

제목 : Summary notes from Martin Fowler's PoEAA

도메인 로직을 구성하는 패턴
1. 트랜잭션 스크립트 (Transaction Script) : 프로시저럴, 많은 중복, 모든 개발자가 이해하기 쉽고 간단합니다.
2. 테이블 모듈 (Table Module) : .NET 에서의 DataSet 처럼 트랜잭션 스크립트와 도메인 모델 사이의 중간이며, 도메인 로직을 핸들링하기가 트랜잭션 스크립트 보다 쉽습니다.
3. 도메인 모델 (Domain Model) : 복잡한 비즈니스 로직을 가지는 프로젝트에 적당합니다. 코드 중복 문제를 해결합니다.


데이터 소스로 부터 간단한 입출력만을 하는 프로젝트의 경우에는 트랜잭션 스크립트가 적합 할수 있으며, 복잡한 비즈니스 로직을 처리해야 한다면 도메인 모델이 적합합니다.


아이덴티티 맵 (Identity Map)

이름 그대로 오브젝트들을 구분할수 있는 Map, 오브젝트에 아이디를 부여 하여 메모리에 로딩 하여 맵으로 가지고 있는 형태. Look Up 하여 오브젝트를 참조할 수 있도록 지원.
메모리에 로드된 오브젝트는 상태가 변경될 수 있는데, 그 변경된 상태에 대해서도 다른 오브젝트에서 접근 가능해야 한다. 아이덴티티 맵은 세션 스코프를 가지며, 각각의 세션 내에서 인스턴스는 격리됨을 보장 받아야 합니다.
Unit of Work를  사용한다면 아이덴티티 맵이 위치할 수 있는 최적의 장소이며, Unit of work를 사용하지 않는 다면 세션에 결합된 Registry 가 적절한 장소입니다.

아이덴티티 맵의 다른 가치는 데이터베이스에 대한 캐쉬 역할을 하는 것입니다.


게이트웨이 (Gateway)
외부 시스템 또는 자원에 대한 접근을 캡슐화 하는 오브젝트. API와 같은 외부 자원을 취급하는데 좋습니다. API 코드들을 감쌈으로써 일반적인 클래스의 인터페이스로 보이게 할 수 있습니다.

 

분리된 인터페이스 (Separated Interface)
구현으로부터 분리된 인터페이스를 정의 합니다. 인터페이스 정의는 어떤 패키지에서 사용하고, 다른 패키지에서는 구현을 합니다. 이러한 구현은 클라이언트의 인터페이스에 대한 의존을 구현으로 부터 완벽히 분리할 수 있습니다.

분리된 인터페이스의 구현은 팩토리 오브젝트(Factory Object)를 이용하여 로드할 수 있습니다. 컨파일-타임(compile-time) 의존을 제거 하기 위해서 팩토리는 리플렉션(Reflection)을 이용하여 런-타임(run-time)에 구현을 로드할 수 있습니다.


이 패턴은 두 부분으로 구성되는 시스템에서 의존을 제거하기 위해서 필요하다. 예를 들어 도메인 코드가 데이터 매퍼(Data Mapper)를 호출하는 것 처럼 한 레이어에서 다른 레이어의 코드를 호출할 때 필요합니다.


레지스트리 (Registry)
오브젝트 또는 서비스를 다른 오브젝트들이 사용할 수 있도록 잘 알려진 공용 오브젝트.


특수 케이스 (Special Case)
특수한 경우에 특별한 행위를 제공하는 클래스. 일반 어플리케이션에서 null 값을 검사하는 패턴 등에서 이용할 수 있습니다. 다수의 경우에 매우 많은 곳에서 null 값을 검사할 필요가 있다면 이 패턴을 이용할 수 있습니다.
null 값을 반환하는 대신에 호출자가 기대하는 특별한 오브젝트를 반환할 수 있다. 단, 반환되는 오브젝트는 해로운 행위를 하지 않아야 합니다.


플러그인 (Plugin)
컴파일시가 아닌 설정(Configuration)에서 클래스를 연결 하는 것. 런타임 환경에서 다른 행위가 요구되어 질때 이 패턴을 사용 할 수 있다. 설정은(Configuration)은 리빌드 또는 재배포를 요구하지 않아야 합니다.


레이어링 (Layering)
관계 데이터를 단순히 보여주고, 입출력을 수행하는 프로젝트에서는 레이어링을 적용할 필요가 없습니다. 문제는 도메인 로직(비즈니스 규칙, 유효성 검사, 연산을 수행하는)에서 발생합니다. 클라이언트 UI에 포함된 비즈니스 코드들은 도메인이 복잡해 감에 따라 함께 일하기가 매우 어려워지고, 중복된 코드들을 양산합니다. 이 말은 작은 변경에도 수많은 화면이 문제를 일으킬수 있음을 의미합니다.

대안으로은 비즈니스 로직은 저장프로시저(Stored Procedure) 에 집어 넣는 것입니다. 저장프로시저는 서투른 코드를 유발하는 제한된 구조화된 메커니즘을 제공합니다.

교과서적인 3-Layer는 프리젠테이션, 도메인, 데이터소스 입니다.

 

레이어드 디자인

웹 어플리케이션에서의 레이어드 디자인과 Cross-Cutting Concern.


 


신고

이 글을 Twitter / Facebook 에 공유하기
이 글이 유익하다면 아래의 트위터 버튼을 눌러 공유해 주시거나, 페이스북 "좋아요" 버튼을 눌러 주세요.

   


Posted by 반더빌트


티스토리 툴바