안드로이드 폰의 전격 발매를 시작으로 스마트폰 시장은 애플, 구글, 마이크로소프트의 삼분지 천하가 모습을 드러내고 있습니다.



2010.03.30 일  예전 블로그에 작성되었던 글 이전 한 것입니다.


1. 스마트폰 OS의 삼분천하


애플은 아이폰을 이용해 수년간 발전시켜 오면서 '풍부한 사용자 경험(Rich User Experience)란 이것이다' 를 보여줌과 동시에 , 모든 개발자가 배포자가 될 수 있는 앱스토어로 어플리케이션 생태계를 만들어 냈습니다.

개개의 개발자가 참여하는 오픈소스라는 형태가 이미 존재 하였지만, 그 결과물로 돈을 만들어 낼수 있는 생태계를 만든 것은 애플입니다.

애플은 그 특유의 천재성인 '손 맛' 을 극도로 실현시키는 재능을 가지고 있으며, 아이패드의 라인업 추가는 또 한번 커뮤니케이션 디바이스의 혁명을 보여주려 하고 있습니다.

이로써 애플은 스마트 인포메이션 디바이스 경쟁에서 당당히 챔피언 자리를 차지하고 있습니다.



구글의 안드로이드 폰들이 속속 출시 됨에 따라 스마트 인포메이션 디바이스 분야에 구글이 진입하기 시작했습니다.

구글은 정보의 조직화(Information Orgernize)를 존재 가치로 내세우는 회사입니다. 검색엔진 회사가 전혀 다른 분야로 보이는 OS 개발에 뛰어 든 것에 대해서 대다수는 의아할 것입니다. 구글이 정성들여 만든 서비스에 접근하려면 브라우저(크롬)가 있어야 하고, 브라우저를 기동하는 OS가 필요하다는 것이 구글의 생각이었을 것입니다.

자신들이 통제하지 못하는 OS와 브라우저를 거쳐 서비스에 접근 되는 것이 장벽이라 생각 했겠지요. 구글은 이미 성숙해 버린, 그리고 비용이 많이 소요되고, ROI가 보장되지 않는 PC OS를 만들기 보다는 스마트폰 OS에 진출 했지요. 

앞에 열거한 PC OS 진출의 약점이 작으면서도 PC보다 보급률이 몇배나 많은 , 24시간 사람들과 함께 하는 장점을 가진 스마트폰 OS에 말 입니다.

안드로이드의 강점은 아이폰과 쌍벽을 이룰만한 기능과 성능을 가지면서, 공개되어 있어 모든 휴대폰 개발 업체에서 탑재할 수 있고, 이는 아이폰에 필적할 만한 하드웨어가 등장할 수 있는 환경이 조성 되었다는 것입니다.

IBM의 메인프레임을 쓰러트린 MS의 DOS 처럼 말이죠.



마이크로소프트는 '윈도우즈 폰 7' 이라는 스마트폰 OS를 하반기에 정식 릴리즈 할 것이라 합니다. 20여년의 개인용 컴퓨터 OS의 왕좌에 굴림하고 있으면서도 윈도우즈 CE 또는 윈도우즈 모바일은 소비자들에게 그다지 감동을 못주는 물건이었습니다. 이는 '윈도우즌 폰 7'에 대해서 사람들이 별로 관심을 보이지 않게 하는 MS의 커리어 입니다.

그러나, MS에게도 기회는 있습니다. 통합된 개발환경이 그것이지요, 자바가 "Write once, run anywhere" (WORA) 라는 캐치 프레이즈를 가지고 있는 것처럼, 닷넷 개발 플랫폼에서 개발되어지는 것은 run anywhere 라고 말할 수 있을 만큼 MS 진영 개발자가 쉽게 참여 할 수 있다는 것이 장점 입니다. 또한 광범위하게 퍼져 있는  윈도우 OS와 가장 궁합이 잘 맞는다가 장점이겟죠.  하지만 이것으로는 약간 부족합니다. 이제까지 하드웨어가 따라주지 못하긴 했지만 OS 황제의 장점에도 불구하고, 팜(Palm) 등의 기기에 밀려 왔던 것이 MS이니까요.




2. 누가 위나라의 조조가 될 것인가?

결론부터 말하자면 위(안드로이드),  촉(윈도우즈 폰), 오(아이폰)이 될 것이라 조심스럽게 점쳐 봅니다.

거대 동맹군(삼성, 엘지, 모토로라 등등의 Manufacture Alience)을 가지고 있는 구글,  

황제의 후손이고 정통성이 있긴 하지만 뭔가 그럴듯한 성과를 내지 못하고 영토하나를 차지하는 마이크로소프트,
 
하남(河南)에 막강한 수군(사용자 경험)을 가지고 철옹성을 쌓고 있는 애플이 위,촉,오 삼분지 천하를 이루게 될 것이라 예상합니다.


애플은 분명히 현재 최고의 스마트폰 강자 입니다. 아이폰과 관련된 모든 것을 통제하는 것이 애플이지요, 

하지만, 통제되는 세상은 통제할 수 있는 만큼만 성장합니다. 

시장초기에는 뛰어난 리더에 의해서 역량을 한곳에 집중하는 것이 성과를 이룩하지만 시장이 커지면 네트워크화 되는 것이 진리이죠.  
이것이 바로 애플의 강점이자 장점입니다.


스마트폰이 어플리케이션에 의존하는 성격이 있지만, 현재의 정보기기는 연결되어 있지 않으면 깡통이라는 것을 누구라도 알고 있습니다. 인터넷이 연결되어 있지 않은 컴퓨터는 타자기 또는 동영상 재생기 일 뿐입니다. 그 연결 되어 있음을 가장 잘 지원할 수 있는 진영이 누구냐, 그 주인공은 구글 안드로이드폰이 될 것입니다.

구글이 그것에 집중 하고 있다는 것을 알 수 있는 것이 바로 안드로이드 폰 2.0 의 가장 중요 기능인 Accounts, Contacts and Sync 입니다.




안드로이드 2.0 비디오와 안드로이드 개발자 센터 


외국의 Web 2.0 소셜이라는 서비스들은 회원을 가입할때 , 지메일 또는 페이스북에 친구로 설정되어 있는 사람들에게 가입 사실을 알리고, 초대 하기를 유도합니다. 무심코 '다음' 버튼을 클릭하면 모든 친구들에게 메일이 발송됩니다. 바이러스가 전파 되듯이 말이죠.

안드로이드폰은 개통시에 구글 메일 계정을 등록하기를 요구합니다.

이 계정을 이용하여 안드로이드폰 2.0의 Accounts, Contacts and Sync가 제대로 작동되는 것이지요.

모든 연결은 구글을 통하여 라는 '팍스 구글리카'의 시작이자 완성입니다.


이제 별도의 개인정보관리시스템(PIMS) 어플리케이션이 필요없게 됩니다. 친구들이 자신의 정보를 업데이트 하기만 한다면 , 주소, 전화번호, 상태 변경은  구글 Account를 이용해서 Contact 하고 Sync 될 테니깐 말이죠.


전세계의 데이터 센터와 연동되는 안드로이드는 팍스 구글리카를 완성하는 무시무시한 물건이 되리라 생각합니다.

 

 

 

핵심은 이게 아니네요, 

 

통제에 의한 혁신은 시장 초기에 효율을 발휘 하고,

분산에 의한 혁신은 시장 성숙기의 완성 모델이다.

 

이것도 물론 대혁신 이후 되풀이 되겠지만.


 

저작자 표시
신고

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

   


Posted by 반더빌트
.NETFramework 3.5 에서 EntityFramwork 사용 프로젝트를 만들었을때 아래와 같은 메시지를 받을 때가 있음.


컴파일러 오류 메시지: CS0012: 'System.Data.Objects.DataClasses.EntityObject' 형식이 참조되지 않은 어셈블리에 정의되었습니다. 'System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' 어셈블리에 참조를 추가해야 합니다.



.NET 3.5 설정의 Web.Config  assemblies 섹션에 아래의 어셈블리 참조가 기본값으로 들어가지 않기 때문입니다.

<add assembly="System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

다음과 같이 넣어 주세요.

    <!-- 
            컴파일된 페이지에 디버깅 기호를 삽입하려면
        compilation debug="true"를 설정하십시오. 이렇게 설정하면
        성능에 영향을 주므로 개발하는 동안에만
        이 값을 true로 설정하십시오.
    -->
    <compilation debug="true">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
        <add assembly="System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
      </assemblies>
    </compilation>

 
저작자 표시
신고

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

   


Posted by 반더빌트
이전 포스트 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 반더빌트
또 등장 했습니다. "역전 inversion" 이라는 말은 용어 자체가 굉장히 어려운 말입니다. 두가지 뜻을 담고 있으니까요. "역전" 은 두가지를 알아야 이해를 할 수 있는 용어 입니다. "기존"의 상태 와 "역전" 된 상태, 그리고 역전되어야 하는 이유를 알아야 합니다. 이 포스트는 바로 마틴 파울러 martin fowler 의 "제어의 역전 IoC Inversion of Control" 의 문서를 가지고 이야기 합니다.

* IoC 와 IoC Container는 조금 다른 개념으로 다음 포스트에 다룰 예정입니다. 


프로그래밍이란 제어를 순서화 하는 일련의 과정 입니다. 하나의 프로그램은 수천, 수만개의 제어를 가질 수 있습니다. 프로그래머가 다음에 일어날 모든 제어를 알고, 관리한다는 것은 불가능에 가깝습니다. 관련이 깊은 제어와 속성들을 하나로 묶어 객체를 만들고, 객체들이 상호작용 하도록 하자는 것이 객체-지향의 개념이라고 할 수 있습니다. 단위 제어를 객체에 묶었어도 여전히 객체들을 제어 해야 합니다. 어떻게면 프로그래머가 작성하는 코드가 제어의 숫자를 줄일 수 있게 할까?에 대한 고민이 바로 "제어의 역전" 입니다.

재사용 가능한 프로그램을 작성하고 싶다고? 콘트롤을 줄여!



제어의 역전 이란 어떠한 일을  하도록 만들어진 프레임워크에 제어의 권한을 넘김으로써 클라이언트 코드가 신경 써야 할 것을 줄이는 전략입니다. 이것을 제어가 역전 되었다 라고 합니다. 일반적으로 라이브러리는 프로그래머가 작성하는 클라이언트 코드가 라이브러리의 메소드를 호출해서 사용하는 것을 의미 합니다. 프레임워크를 규정하는 특성은 프레임워크의 메소드가 사용자의 코드를 호출 한다는데 있습니다.

여기까지는 이해가 쉽지만, 의문이 생깁니다. 대체 어떻게 프레임워크가 나의 메소드를 호출하지? 전통적인 사고 방식을 가진 우리 프로그래머들은 혼란이 발생합니다.

어떻게 하면 프레임워크가 나의 코드를 호출 할 수 있을까요? 프레임워크는 내가 작성한 코드를 모르잖아!. 

제어를 역전 시키는 (프레임워크가 나의 코드를 호출 할 수 있게 하는) 가장 쉽게 생각할 수 있는 접근 방법은 프레임워크의 event, delegate 에 나의 메소드를 등록 시키는 것입니다. 전달되는 인자와 반환 형식만 일치 한다면, 프레임워크 코드는 내가 작성한 객체와 타입을 고려하지 않습니다. 등록된 메소드만 감지하여 실행 invoke 하는 것입니다.

다른 방법은 프레임워크에 정의 되어 있는 인터페이스 interface, 추상타입 abstract 을 나의 코드에서 구현, 상속 한후 프레임워크에 넘겨주는 것입니다. 프레임워크는 인터페이스와 추상을 알고 있으므로  내가 하고자 하는 일련의 작업을 처리할 수 있습니다. 이는 객체를 프레임워크에 주입하는 것이고, 이를 의존을 주입 dependency injection 한다고 합니다.


마틴 파울러는 그의 글 Inversion of Control Containers and the Dependency Injection pattern 에서 IoC 라는 용어가 너무 일반적이며 모호하므로 앞으로는 Dependency Injection 이라는 용어를 사용하겠다 라고 말하고 있습니다. 만, 이 포스트에서는 IoC 의 원래의 개념 설명에 집중하도록 하겠습니다.


좀더 깊은 이해를 위한 마틴파울러 글의 요약 및 정리는 아래에 이어 집니다.

Inversion of Control , 2005 june 26, by martin fowler

 제어 역행은 여러분이 프레임워크를 확장할 때 종종 만나게 되며, 프레임워크의 특성을 정의 합니다. 사용자로 부터 몇가지 정보를 얻는 커멘드라인 프로그램을 작성한다고 상상해 보세요. 다음과 같을 것입니다.

#ruby
  puts 'What is your name?'
  name = gets
  process_name(name)
  puts 'What is your quest?'
  quest = gets
  process_quest(quest)



이 상호 작용에서 코드는 제어를 하고 있습니다. : 언제 질문을 할 것인가? 응답을 언제 읽을 것인가?  이 결과들을 언제 처리할 것인가? 를 결정합니다.


그러나, 윈도윙 시스템을 위한 작업을 한다면 다음과 같을 것입니다.

require 'tk'
  root = TkRoot.new()
  name_label = TkLabel.new() {text "What is Your Name?"}
  name_label.pack
  name = TkEntry.new(root).pack
  name.bind("FocusOut") {process_name(name)}
  quest_label = TkLabel.new() {text "What is Your Quest?"}
  quest_label.pack
  quest = TkEntry.new(root).pack
  quest.bind("FocusOut") {process_quest(quest)}
  Tk.mainloop()


이 두개의 프로그램은 "제어의 흐름 flow of control" 에서 매우 큰 차이가 있습니다. 커맨드 라인 프로그램에서는 
process_name 과 process_quest  메소드를 호출 합니다. 하지만 윈도우 시스템에서는 폼을 만들 때의 바인딩을 기반으로 나의 메소드를 호출합니다. 제어는 역전 inverted 되었습니다.  내가 아니라 프레임워크가 나를 호출하는 것 입니다. 이 현상을 제어의 역전 Inversion of Control 이라고 합니다.( A.K.A Hollywood Principle - "Don't call us, we'll call you").


프레임워크의 중요한 특성은 사용자가 정의한 메소드가 사용자의 어플리케이션 코드가 호출 하기보다 종종 프레임워크로 부터 호출 되어 진다는 것이다.  프레임워크는 종종 어플리케이션의 행위를 재배치 하고, 순서를 정하는 메인프로그램의 역할을 담당한다. 역전된 제어는 프레임워크가 확장된 뼈대를 제공하는 힘을 제공한다. 사용자는 특정 어플리케이션을 위한 커스터마이즈된 알고리즘 메소드를 프레임워크에 전달한다. 

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

-- Ralph Johnson and Brian Foote 


역전된 제어는 프레임워크와 라이브러리를 구분하는 열쇄 입니다. 라이브러리는 여러분이 호출할 수 있는 기능의 집합 set of functions 니다. 오늘날에는 보통 클래스들로 구성하죠.  각각의 호출은 지정된 일을 한후 클라이언트에게 제어를 반환 합니다. 

 
프레임워크는 추상화된 설계와 많은 내장된 행위를 감싸고 있습니다.  그걸 이용하기 위해서는 여러분이 작성한 클래스를 서브클래싱 또는 플러그 함으로써  행위를 프레임워크의 여러장소에 삽입해야 합니다.  그후 프레임워크는 여러분의 코드를 호출 합니다.

여러분의 코드가 호출 되도록 플러그 하는 방법은 여러가지가 있습니다. 위의 Ruby 에서 처럼 이벤트 이름과 Closure 인자를 넘김으로써 메소드를 바인딩 할 수 있습니다. text entry box 가 이벤트를 감지 했을때 clossure는 코드를 호출 합니다. 이 방법은 매우 편리하지만 모든 언어들이 지원하는 것은 아닙니다.

다른 방법으로는 프레임워크가 이벤트를 정의하고, 클라이언트 코드가 이벤트를 구독 subscribe 하게 하는 것입니다. .NET 플렛폼은 사람들이 이벤트를 정의할 수 있도록 언어에서 지원하는 좋은 예 입니다. 여러분은 delegate를 이용하여 여러분의 메소드를 event 에 바인딩 할 수 있습니다.

위의 예는 한가지 경우에 작동하는  접근방법입니다. 하지만 여러분은 때때로 여러개의 메소드가 호출되기를 원할 것입니다. 이 경우에 클라이언트 코드가 관련된 호출을 받을 수 있는 인터페이스 interface를  구현할 수 있도록 
 프레임워크는 인터페이스 interface를 정의 합니다.


EJB는 이런 스타일의 역전된 제어를 제공하는 좋은 예 입니다. 세션빈을 작성할 때, 여러분은 EJB 컨테이이너가 라이프사이클의 여러곳에서 호출 할 수 있도록 다양한 메소드를  구현할 수 있습니다. 예를 들어 세션빈은 (두번째 저장소를 위한) 
ejbRemoveejbPassivate 와 (대기상태에서 리스토어를 위한) ejbActivate  인터페이스를 정의 합니다. 여러분은 이 메소드들을 호출하는 제어권을 가지지 않습니다.  프레임워크가 알아서 합니다. 우리가 호출 하는 것이 아니라, 컨테이너가 우리가 작성한 코드를 호출 합니다.


이는 역전된 제어의 복잡한 예시 입니다. 이 효과를 더욱 단순하게 얻는 방법이 있습니다. template method 가 좋은 방법입니다 : 슈퍼클래스는 제어의 흐름 flow of control 을 정의하고, 서브클래스는 메소드를 오버라이드 하거나 추상메소드를 구현함으로써 확장을 얻을 수 있습니다. JUnit 프레임워크는 setUp과 tearDown 메소드를 호출 함으로써 여러분의 단위테스트를 생성하고 제거 합니다. 프레임워크는 여러분의 코드를 호출하고 반응하게 합니다. - 또 다시 제어는 역전 되었군요. 


오늘날 IoC 컨테이너의 등장으로 역전된 제어 Inversion of Control 의 의미에 몇가지 혼동이 생겼습니다. 어떤 사람들은 역전된 제어 스타일의 일반적인  원칙( 의존 주입과 같은) 에 혼란 스러워 합니다. IoC 컨테이너가 일반적으로 EJB의 경쟁자로 인식되기 때문에 그 이름이 혼란의 원인이 됩니다. EJB는 여전히 역전된 제어를 매우 많이 사용 합니다.


어원 :
내가 아는  역전된 제어 Inversion of Control  용어는 1998, Object-Oriented Programming 저널에 출판된 Johnson 과 Foote's의 논문 Designing Reusable Classes 에서 입니다. 이 논문은 15년이 지난 지금도 읽어볼 만한 가치가 있습니다. 이 용어가 무엇으로 부터 영감을 받았는지 그들도 기억하지 못하지만, 객체-지향 커뮤니티와 GoF의 책에서 살아남아 등장합니다.  더 섹시한 용어인 'Hollywood Principle' 은  1993, Mesa 의  Richard Sweet 의 논문으로 부터 기인합니다. 그가 쓴 "Don't call us, we'll call you (Hollywood's Law)"의 디자인 목적은 : 사용자가 도구에 대해 희망하는 커뮤니케이션을 도구가 고지 notify 해야 한다는 것입니다. 모델에 대한 명령 command 과 실행 execute 을 사용자가 직접 하는 대신에 말이죠. John Vlissides는 column for C++ report에 'Hollywood Principle'에 대한 매우 좋은 설명을 제공합니다. (어원에 대해 도움을 준 Brian Foote와 Ralph Johnson에게 감사합니다.)
 

### 끝.

관련 포스트 :  

 IoC 의 기반 개념을 가진 : 의존 관계 역전의 법칙(DIP) 

참조
Inversion of Control , 2005 june 26, by martin fowler
- Johnson 과 Foote's의 논문 Designing Reusable Classes 은 읽어볼 만한 굉장한 가치가 있습니다. 
저작자 표시
신고

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

   


Posted by 반더빌트
새로운 서비스가 어떻게 네이버, 옥션, G마켓을 뛰어 넘을 있는가? 이 포스트는 네트워크 구조에 대한 이해를 통해 그 과정을 살펴 볼 것입니다.

새로운 서비스를 만들고 있는 사람들의 목표입니다. 콘텐츠 포탈을 기획하고 있다면 네이버를, 온라인 커머스를 만들고 있다면 옥션이나 지마켓을 뛰어 넘거나 그에 준하는 서비스를 만들고 싶은게 목표일 것입니다.

하지만, 모든 꿈이 그러하듯 대부분의 후발 서비스는 대단한 아이디어를 가지고 있다 하더라도 골리앗 앞에 무너지고 마는 것을 우리는 수없이 
목격해 왔습니다. 무엇이 문제였을까요? 어떻게 시작해야 할까요? 새로운 후발 서비스는 성공할 가능성이 없는 걸까요? 검색의 후발 서비스 였던 구글은 어떻게 성공했을까요?

지금 부터 말하는 것은 10여년에 걸쳐 얻은 웹에 대한 기반 지식 및 여러 서적[특히 알버트 라즐로 바라바시의 링크], 자료로 부터 얻은 생각입니다. 저의 의견이 전적으로 옳다고 주장하지 않음을 염두에 두고 읽어 주시기 바랍니다.

온라인 서비스를 만든다면 기본적으로 네트워크의 이해로 부터 시작해야 합니다. 웹 자체가 네트워크 임에도 우리는 네트워크를 공부하는데 인색했습니다. 네트워크는 노드와 (node) 링크로 (link) 구성되어 있습니다.  여러분의 웹사이트, 서비스, 콘텐츠는 노드에 대응되며 그를 연결하는 URL이 링크 입니다.  여기까지 이해 했고, 서비스를 성공의 최종 형태에 대한 결론부터 말하자면 보즈-아인슈타인 응축 (Bose-Einstein condensation) 입니다. 아인슈타인을 언급하니 뜬금없다고 느껴지시겠지만 양자역학에 깊숙히 들어가는 것이 아니니 안심하셔도 좋습니다. 

보즈-아인슈타인 응축 BEC, 기체온도를 절대온도 0에 가깝게 내리면 제각기 움직이던 기체원자가 하나의 원자처럼 움직임.



다음의 내용은 네트워크 구조에 대해 컴퓨터 사이언스를 공부하지 않은 
일반인이 가장 쉽게 이해할 수 있도록 쓰여진 알버트 라즐로 바라바시의 책 [링크 Linked The New Science of Networks]를 기반으로 말하겠습니다.

여러분의 서비스, 블로그, 웹사이트, 트위터는 그를 발행하는 순간 네트워크의 구성원이 됩니다. 가지고 있는 지식과 노력을 총동원 하고, 노력한 덕에 허브(많은 링크와 팔로워를 얻은 노드) 가 되었을 수도 있고, 인기 없고 손익 분기넘을 넘지 못한 변방의 노드일 수도 있습니다. 성공 했으면 그 자리를 지키기 위해, 성공하지 못하고 있다면 성공하기 위해 네트워크와 성장 구조를 알아야 합니다.



네트워크 구조의 가장 쉬운 이해
 
세상 만물은 네트워크로 구성됩니다. 수만개의 부품으로 하나의 모습을 만들어내는 자동차 부터, 인간관계 까지 모든 것은 네트워크 입니다. 웹을 네트워크로 변환하면, 인간 자체를 노드로 대입한 서비스가 SNS social network service 입니다. 페이스북이나 트위터의 프로필과 트윗들은 그 사람의 성격을 나타내면서 노드 node 이고, 친구로 삼거나 팔로우 하는 행위를 통해 관계 또는 연결을 만드는 행위로써 링크 link 를 겁니다.


네트워크는 어떻게 구성될까요? 우리가 볼수 있는 네트워크는 매우 복잡하며, 구성되는 기준을 발견하는 것은 거의 불가능해 보입니다. 매우 복잡한 현상에 대한 가능한 가장 단순한 설명이 바로 에르되스 와 레니의 '무작위성 이론' 입니다.



에르되스와 레니의 무작위 네트워크

에르되스와 레니는 그 다양성에 대해 의도적으로 논외로 하고 자연이 따를 수 있는 가장 단순한 해결책을 제안했다. 노드들을 무작위적으로 연결하라, 그들은 네트워크를 형성하는 
가장 단순한 방법으로 주사위를 이용하는 것이라고 결정했다. ~ 에르되스와 레니는 그래프와 그것이 표현하는 이 세상을 근본적으로 무작위적인 것으로 보았다 

링크드, p35

네트워크의 구조를 에르되스와 레니의 생각처럼 무작위성으로 가정하는 것이 가장 이해하기 쉽습니다. 무작위 네트워크 모델의 전제는 철저하게 평등주의입니다. 사람들의 사교 모임인 칵테일 파티에서 몇명과 친구가 되었는지 물어 본다면 거의 같은 수의 친구를 만들었으며, 평균적인 사람보다 상당히 많거나 적은 수의 링크를 가진 사람은 기하급수적으로 드물다는 것을 예측할 수 있습니다. 이를 포와종(Poisson) 분포를 따른다고 합니다. 무작위 네트워크는 이해하기도 쉽고, 상당부분 맞아 보입니다. 우리는 네트워크의 노드가 되었고, 열심히 노력하면 인기 있는 트위터리언이 될 수 있고, 만들고 있는 서비스는 머지 않아 성공할 것 같은 생각도 들고, 모든 노드들은 평등하다란 무작위 네트워크의 가정을 공유합니다. 네트워크야 말로 세상에 존재하는 가장 평등한 미디어라는 가정까지 나가기도 합니다.


최근까지만 해도 우리는 상호연관된 세계를 기술할 다른 대안을 갖고 있지 못했다. 따라서 네트워크를 모델링함에 있어서 무작위 네트워크는 우리의 사고를 지배해왔다. 복잡한 현실 세계의 네트워크들은 근본적으로 무작위적인 것으로 간주되었다.

링크드, p47


여기에 1967년 하버드 대학 교수였던 스탠리 밀그램(Stanley Milgram)에 의해 실험된 6단계만 거치게 되면 아는 사람과 연결될 수 있다는 "여섯 단계의 분리 Six Degrees of Seperation" 가 가져온 "좁은 세상 Small Word"라는 개념은 여러분의 서비스와 블로그를 열심히 홍보하고 좋은 아이디어를 가졌다면 성공할 수 있을 거란 믿음을 더욱 공고히 합니다.
 

모두 같은수의 연결을 가진 네트워크에서 연결수가 다른 무작위 네트워크




하지만 현실세계는 달랐다.

세상은 6단계만 거치면 아는 사람과 연결될 수 있고, 모든 노드가 평등하다면 내 블로그나 서비스가 성공할 가능성은 충분히 높습니다. 하지만, 현실 세계에서는 새로운 서비스들은 실패해 나가고, 내 블로그의 하루 방문자는 몇명에 그치며, 트위터의 유명인 만큼의 팔로워를 얻을 수도 없습니다. 뭐가 잘 못 되었을까요?


임의의 두 페이지 간의 거리 측정하기

우리의 사회와는 달리 웹은 디지털이다. 이는 하나의 컴퓨터 프로그램을 짜서 자동적으로 문서들을 다운로드하고, 또 거기에 포함된 모든 링크들을 추출하여 그것이 가리키고 있는 문서들을 모두 방문하여 다운로드하고, 이런식으로 웹상의 모든 페이지들이 포착될 때까지 계속하도록 만들 수 있다는 것을 의미한다. 이런 프로그램을 풀어놓으면 이론상으로 웹에 관한 완벽한 지도를 얻을 수 있다. 컴퓨터 세계에서는 이러한 종류의 소프트웨어를 로봇 또는 크롤러라고 부르는데 사람의 개입 없이 자동적으로 웹을 온 사방으로 기어 다니기 때문이다.
~

밀그램의 실험에서 어떤 편지들은 목표인물에게 두 단계를 거쳐서 도달된 반면 어떤 것들은 11단계나 거쳐서 도달했던 것처럼 우리의 연구 결과도 웹 문서들 간의 거리에 상당히 큰 편차가 있음을 보여 주었다.


전체 웹 문서의 수는 NEC 연구소에 따르면, 1998년 말 현재 공개되어 있어서 인덱싱이 가능한 웹 문서의 수는 대략 8억 노드 정도라고 한다. ~ 여러분이 웹을 서핑하면서 받는 직관적 인상과는 달리 웹은 실제로는 또하나의 좁은 세상이다. 어떤 문서도 평균적으로 다른 문서와 19클릭 정도밖에 떨어져 있지 않은 것이다.

링크드, p60, p61, p63


매우 넓어보이는 웹이 또하나의 좁은 세상이라면 우리의 무작위 모델은 틀리지 않아 보입니다. 근데 뭐가 문제라는 걸까요?


웹은 좁은 세상인가? 넓은 세상인가?
 
여섯 단계 또는 열아홉 단계라는 문구는 자칫하면 '좁은 세상'에서는 사물들을 쉽게 찾을 수 있다는 잘못된 오해를 낳을 소지를 갖고 있다. ~ 열아홉 단계의 거리에 있는 노드에 도달할 때쯤이면 우리는 논리상 10에 16승의 문서들을 뒤져본 셈이 될 것이다. ~ 하나의 문서를 체크하는데 1초가 걸린다고 가정해도 19클릭 거리에 있는 문서들을 다 체크해 보는데에는 3억년 이상이 걸린다.

링크드, p68


좁은 세상이라고 여겨진 듯 했던 웹이 실제로 넓은 세상이었습니다. 또한 네트워크를 좀더 자세히 관찰해 보니, 연결선 수가 극히 많은 허브와 커넥터가 존재 했고 이 노드들에 대한 

설명은 무작위적 세계관 전체를 완전히 폐기하도록 요구했습니다.


웹은 민주적이며 평등한가?

사이버스페이스는 궁극적인 언론의 자유를 구현하고 있다. 어떤 사람은 그것에 대해 화낼 수도 있고 어떤 사람은 그것을 좋아할 수도 있지만 웹페이지의 내용을 검열한다는 것은 매우 어려운 일이다. 일단 출판되면 그것은 수십억 명이 볼 수 있다. 이처럼 유례 없이 커진 표현의 권리와 낮은 출판비용 때문에 웹은 민주주의의 궁극적 토론의 장이 되고 있다. 모든 사람의 목소리는 모두 다 균등한 기회를 갖고 있다. 적어도 헌법학자들이나 그럴듯한 비즈니스 잡지들은 그렇게 주장한다. 웹이 무작위 네트워크라면 그들의 주장이 맞을 것이다. 

하지만 웹은 무작위 네트워크가 아니다. 우리의 웹지도 만들기 프로젝트의 결과중 가장 흥미로운 점은 웹에는 민주주의, 공정성, 평등성이 완벽하게 존재하지 않는다는 것이다.

~ 중요한 문제는 당신이 어떤 정보를 웹에 출판했을 때, 수십억 페이지나 되는 이 문서의 정글에서 어떤 사람이 그것을 알아챌 수 있는가이다.

링크드, p97


이제 우리의 웹이 무작위적이지 않다는 것과 노드들이 평등하지 않다는 것을 알았습니다. 우리는 무작위적 네트워크 모델을 포기할 수밖에 없습니다.그렇다면 우리가 출판한 정보를 사람들이 어떻게 접근하게 할 수 있을까요? 구글과 같은 성능좋은 검색엔진이나 네이버와 같은 많은 사람이 이용하는 검색엔진에 의지하면 되지 않을까요? 


웹문서의 가시성

누군가가 그것을 읽기 위해서는 먼저 그것이 가시적이어야 한다. 웹에서 가시성(visibility)의 척도는 바로 링크의 개수이다. 당신의 웹페이지로 들어오는 링크 (incoming link)가 많으면 많을수록 그것은 가시적이다. ~ 하지만 평균적인 웹페이지는 단지 다섯개에서 일곱 개의 링크만을 갖고 있고, 그 각각은 수십억 페이지 중 하나를 향하고 있다. 따라서 다른 웹 문서들이 당신의 웹페이지를 링크할 가능성은 거의 제로에 가깝다고 해야 할 것이다. 
~
웹의 구조는 연결선 수가 매우 많은 극소수의 허브(hub)에 의해 지배되고 있음을 알 수 있다.
~
실용적인 측면에서 보자면 두세 개 정도의 다른 문서들만이 링크하고 있는 페이지들은 사실상 존재하지 않는 것과 마찬가지 이다.

링크드, p98, p100


대부분의 검색엔진은 웹 전체를 커버하려는 시도조차 하지 않는다. 그 이유는 단순한다. 가장 많은 검색결과를 가져다 주는 검색엔진이 반드시 최선의 것은 아니기 때문이다. ~ 아무리 단순한 질의에도 웬만한 검색엔진이라면 수천 건의 검색 결과를 보여주는 것에 우리는 익숙해져 있다 .여기에다 수백만 건을 추가 하는 것은 전혀 무의미한 일이다.

링크드, p272 
 
 

내가 그렇게 열심히 정보를 출판하고 있음에도 내 서비스나 블로그는 존재 하지 않는 것과 같다고? 새로운 서비스들이 실패해 나가고, 내 블로그의 방문자가 적은 이유를 납득할 수 있겠어, 그럼 아예 방법이 존재하지 않는 것일까? 참, 허브와 커넥터라는 것이 존재하잖아! 내가 허브가 될 수는 없는 것일까? 허브는 어떻게 만들어 지는 것일까?

네트워크를 좀더 자세히 관찰한 결과 웹페이지들이 갖고 있는 링크의 분포는 멱함수 법칙(Power Law)이라 불리는 수식을 정확하게 따르고 있음을 발견하게 되었습니다. 멱함수 법칙을 따르는 네트워크를 척도 없는 네트워크 모델 (Scale-Free Network Model) 이라고 합니다.



웹의 불균등성

불균등성(uneveness)이 멱함수 분포를 가진 네트워크의 특징이다. 멱함수 법칙은 대개의 현실 네트워크에서 대다수의 노드들은 소수의 링크만을 갖고 있고, 이러한 다수의 노드들이 이례적으로 많은 링크들을 갖고 있는 소수의 큰 허브들과 공존하고 있다는 사실을 수학적 공식으로 표현한 것이다. 작은 노드들 상호간을 연결하는 소수의 링크로는 네트워크 전체를 연결시키기에 역부족이다. 전체 네트워크가 분절화 되는 것은 막는 기능은 몇몇 허브들의 몫이다.

링크드, p118


자신들에게 들어오는 링크가 많지 않다면 검색될  가능성은 거의 없다. 반면 허브는 시간이 갈수록 집중도가 더욱 높아지기 마련이다 .

링크드, p288
 

멱합수 Power law가 작동하는 네트워크는 많은 링크를 가진 소수의 허브와 소수의 링크를 가진 대다수의 노드로 구성된다.




허브와 커넥터는 어떻게 태어나는 것일까?

현실 네트워크는 척도가 없는 네트워크라는 것을 알았습니다. 이제 우리의 서비스와 블로그가 허브가 되길 목표하고 있습니다. 그럼 허브와 커넥터는 어떻게 만들어 지는 것일까요? 현실 네트워크는 그 다양성에도 불구하고 모두 한가지 본질적인 특징을 가지고 있습니다. '네트워크는 성장한다' 라는 것이지요. 모든 네트워크는 몇개의 노드로부터 시작해서 새로운 노드가 추가 되면서 점차적으로 성장하여 현재 상태에 이르렀습니다. 성장(growth)이라는 것을 도입하게 되면 무작위적 세계가 가정하고 있던 네트워크는 정적(static)이라는 가정을 몰아내게 됩니다. 우리가 일반적으로 링크하는 웹페이지는 보통의 노드들이 아닙니다. 그들은 허브라는 것 입니다. 그들이 잘 알려져 있으면 있을수록 더 많은 링크들이 그들을 향합니다. 결국 우리는 모두가 잘 아는 노드를 링크하는 무의식적인 편향을 따르는데, 웹상에서 이러한 노드는 곧 연결선 수가 많은 노드들입니다. 즉, 우리는 허브를 선호하는 것입니다. 웹상에서 어디를 링크할 것인가를 결정함에 있어 우리는 선호적 연결(preferential attachment) 이라는 방식을 따른다고 할수 있습니다.



성장과 선호적 연결 Growth and Preferential attachment

A.성장 : 각각의 주어진 기간 동안 우리는 새로운 노드들을 네트워크에 추가한다. 여기서는 네트워크가 한번에 하나의 노드씩 짜여진다는 것이 요점이다.

B.선호적연결 : 우리는 각각의 새로운 노드들이 네트워크 내에 의미 있는 노드들 중 2개에 링크된다고 가정한다. 기존의 한 노드를 링크할 확률은 그것이 현재 갖고 있는 링크 수에 비례한다. 즉 2개의 노드중 어느 쪽을 선택할 것인가에 있어서, 하나의 노드가 다른 노드에 비해 2배만큼 많은 링크를 현재 갖고 있다면 새로운 노드가 열결선 수가 많은 그 노드에 링크할 확률이 2배 만큼 크다.

링크드, p145


바로, 성장과 선호적 연결이라는 것을 통해 허브와 커넥터가 태어나는 것입니다.





우리의 서비스는 허브가 될 수 없는 것일까?

멱함수 법칙을 따르는 네트워크에 새로 등장하는 노드가 허브로 발전하는 것은 거의 불가능합니다. 멱함수 법칙이 나타내는 척도없는 모델에는 후발 주자가 지배적이 되는 것을 허용할 수 있는 공간이 없습니다. 우리의 서비스는 허브로 발전할 수 없는 것일까요? 하지만 우리는 후발 주자가 매우 많은 링크를 끌어 모은 경우를 알고 있습니다. 검색 엔진의 후발 주자 였던 구글이 허브로 발전하는 것을 말이죠. 이것은 어떻게 가능했을까요?


후발주자가 지배적으로 되기, 구글

어떤 노드는 아주 늦게 등장했음에도 단기간에 모든 링크들을 긁어들인다. 반면에 어떤 노드는 초창기에 등장했으면서도 선발주자의 지위를 허브로 발전시키는 데 실패하기도 한다. 대부분의 네트워크에 존재하는 치열한 경쟁상황을 설명하기 위해서 우리는 각 노드들이 다르다는 점을 인정해야 한다.

링크드, p158


후발 주자가 허브로 발전될 수 있는 가능성을 있음을 확인했습니다. 노드들은 서로 다르고 경쟁적 환경에서 각 노드들은 어떤 적합성 (fitness)를 갖고 있다는 것입니다. 



적합성 이란?

적합성이란 다른 사람보다 상대적으로 친구를 잘 만드는 사람의 능력이 될 수도 있고, 다른 기업과 비교하여 고객을 잘 끌어들이고 유지하는 기업의 능력이 될 수도 있고, 수십억의 다른 웹페이지보다 많은 관심을 끌어 사람들이 매일 방문하도록 하는 웹페이지의 능력일 수도 있다. 그것은 경쟁적 상황에서의 노드의 능력에 대한 양적 척도이다. 적합성은 사람의 유전적 속성에 기인할 수도 있고, 기업의 제품이나 경영의 품질과 관련된 것일 수도 있고, 배우의 재능과 관련된 것일 수도 있고, 웹사이트의 컨텐츠와 관련된 것일 수도 있다.

~

척도 없는 모델에서 우리는 노드의 매력이 단지 그것의 링크 수에 의해서만 결정되는 것으로 가정 했었다. 경쟁적 환경에서는 적합성도 영향을 미친다. 즉 적합성이 높은 노드는 보다 빈번하게 링크된다는 것이다.

링크드, p159, p160


많은 사람들이 내 웹페이지에 대해 흥미를 느끼고 링크를 연결시킨다면 나의 웹사이트는 소형 허브로 변해갈 것이고 검색엔진도 불원간 그것을 알아차리게 될 것이다.

링크드, p289 


우리의 서비스가 적합성을 가져야 한다는 것을 알았습니다. 적합성이 높다면 상대적으로 낮은 노드들에 비해 더 빠른 속도록 링크를 획득 할 수 있습니다. 따라서 노드가 링크를 획득하는 속도는 더이상 진입한 나이(순서)의 문제가 아니게 되었습니다. 노드가 언제 네트워크에 참여했는가와는 무관하게, 적합한 노드라면 적합성이 낮은 모든 노드들 보다 많은 링크를 획득할 수 있을 것입니다.

하지만, 이것으로는 부족합니다. 몇개의 링크를 얻거나 수백명의 팔로워를 얻는 일은 비교적 쉽습니다. 약간의 인기를 얻어 소형 허브로 발전하였지만 임계점을 넘지 못하고 캐즘에(CHASM : 제품이 아무리 훌륭해도 일반인들이 사용하기까지 넘어야 하는 침체기를 가리키는 경제용어) 빠진 서비스들은  너무 많이 존재 합니다.  우리의 경쟁자는 이미 허브가 된 서비스들 입니다. 이미 허브가 된 서비스들은 적합성을 가지고 있는 동시에 선호적 연결을 가지고 있으므로 경쟁우위에 있습니다. 


제품생명주기와 흡사한 캐즘 그래프는 많은 제품이 얼리아답터 이상의 링크를 확보하지 못함을 의미




결론은 보즈-아인슈타인 응축 Bose-Einstein condensation

웹에는 넘원자라는 것이 없고 네트워크에도 적어도 물리학자들이 의미하는 바의 "에너지 준위" 같은 것이 없습니다. 그렇다면 왜 네트워크에서 보즈-아인슈타인 응축같은 양자역학을 거론하는 것일까요? 


보즈-아인슈타인 응축 Bose-Einstein condensation

보통 온도에서 기체 분자들은 서로 다른 속도를 가지고 서로에게 부딪친다. 즉 어떤 것들은 빠르고 어떤 것들은 느리다. 물리학의 언어로 말하면, 어떤 것들은 높은 에너지를 또 어떤 것들은 낮은 에너지를 갖고 있다. 만약 기체의 온도를 낮추면 모든 원자들이 느려진다. 이들을 완전히 멈추게 하기 위해서는 온도를 현실적으로는 도달 할 수 없는 온도인 절대 온도 0도로 낮춰야 한다. 아인슈타인은 만약 서로 구별되지 않는 원자들로 이뤄진 기체가 충분히 차가워지면 입자들 중 상당 부분이 가장 낮은 에너지 상태에 머물게 될 것이라고 예측했다. 즉, 원자들은 절대 온도보다는 높은 어떤 임계온도에서도 가장 낮은 에너지 상태로 만들 수 있다는 것이다. 입자들이 이러한 상태에 도달 했을때 그들은 새로운 물질을 형성하는데 이를 보즈-아인슈타인 응축물이라고 부른다.

링크드, p166


세상의 모든 물질은 페르미온(fermion)과 보존(boson)이라는 두가지 종류의 입자로 이루어져 있다. 그 중 보존이라는 입자는 온도를 절대0도(영하 273도씨)에 가까운 극저온으로 낮추면 '보즈' 응집상태가 된다.

보즈 응집 상태는 우리가 알고 있는 물질상태인 고체, 액체, 기체나 제4의 상태인 플라즈마도 아니어서 제5의 물질 상태라고 불린다. 일반적으로 기체상태의 원자들은 평균 200~300ms의 속도로 마구잡이로 움직이지만 온도를 절대 온도 0도에 가깝게 냉각시키면 원자들은 마치 한 개의 원자가 움직이는 것처럼 모두 똑같이 움직인다. 동시에 원자를 한곳에 모아주면, 급격히 응축돼 물질의 밀도가 거의 무한대로 올라간다.

네이처 온라인 / 저널 : Preprint 7, Nov 2003 


보즈-아인슈타인 응축과 네트워크는 어떤 관련이 있을까?


비안코니(바라바시의 연구를 도운 조교수)는 단순한 수학적 변환을 통해 적합성을 에너지로 대체하여 적합성 모델에서 각 노드에 에너지 수준을 부여했다. 그러자 갑자기 수식이 예상외의 의미를 갖게 되었다. 80년 전에 아인슈타인이 보즈-아인슈타인 응축을 발견할 때의 그것과 유사한 모양을 갖게 된 것이다. 물론 이것은 그냥 우연의 일치로서  큰 의미가 없을 수도 있었다. 하지만 적합성 모델과 보즈의 기체 간에는 분명 엄밀한 수학적 대응관계가 있었다. 이 대응관계에 따르면 네트워크에서의 각 노드는 보즈 기체에서의 에너지 준위에 대응된다. 노드의 적합성이 높을 수록 그것에 대응되는 에너지 수준은 낮아진다. 네트워크에서의 링크는 보즈 기체에서의 입자에 대응되는데, 그들 각각에는 에너지 수준이 부여된다. 네트워크에 새로운 노드를 추가하는 것은 보즈 기체에 새로운 에너지 수준을 추가하는 것과 같고, 네트워크에 새로운 링크를 추가하는 것은 보즈 기체에 새 입자를 추가하는 것과 같게 된다. 이 대응관계에서 복잡한 네트워크는 거대한 양자 기체와 같은 것이 되며, 거기에서 링크들은 넘원자적 입자처럼 움직인다.

링크드, p168


이 수학적 대응의 결과로 나오는 가장 중요한 예측은 여러분의 서비스나 블로그가 보즈-아인슈타인 응축을 겪을 수 있다는 것입니다. 우리의 서비스가 보즈-아인슈타인 응축을 겪으면 어떻게 된다는 걸까요? 주위의 거의 모든 링크들을 여러분의 서비스로 끌어 모을수 있게 된다는 의미 입니다.


상전이의 결과

보통 자연은 멱함수를 싫어한다. 보통의 시스템들에서 모든 양들은 종형 곡선을 따르며, 상관관계들은 지수의 법칙(Power law)에 따라 급격하게 감소한다. 하지만 시스템이 상전이를 겪고 있을 때 이 모든 것이 달라진다. 이 때에는 멱함수 법칙-혼돈이 가고 질서가 오고 있다는 자연의 신호-이 등장한다. 상전이 이론은 무질서에서 질서 상태로 가는 길은 자기 조직화라는 강력한 힘에 의해 유지되며, 멱함수 법칙에 의해 그 길이 닦여진다는 것을 크고 분명하게 이야기해 주었다. 

링크드, p129


여러분의 서비스는 획기적인 아이디어라는 적합성을 가지고 네트워크에 참여 했습니다. 그 적합성이 높을 수록 링크를 획득하는 속도는 빨라지며, 보즈-아인슈타인 응축을 겪을 환경인 에너지 수준이 낮아 지는 것을 의미합니다.  거기에 에너지 수준을 낮추는 노력의 방향이 맞다면 보즈-아인슈타인 응축이라는 상전이를 겪을 수 있다는 것을 의미합니다. 이 상전이를 겪는 동안 주변의 링크를 한순간에 획득합니다. 이것이 후발주자인 여러분의 서비스가 허브가 되는 과정입니다. 


현실 서비스의 개발은 아이디어가 어떻고, UI 가 어떻고, 유행에 따라 이런 저런 기능을 추가하는 것을 서비스의 성공 방법으로 인식하게 됩니다. 서비스 성공은 결국 서비스가 네트워크의 구성원으로써 적합성을 갖추고 상전이를 일으킬수 있도록 노력을 경주하는 것입니다. 현상에 휘둘리기 보다 네트워크 구조와 허브로 성장하는 조건을 알고 온도를 절대0도로 낮추는 노력을 기울이는 것이 서비스를 성공으로 이끄는 필요조건이라고 할 수 있습니다.


### 끝.

참고

바라바시의 저서 [링크]에는 이 포스트에서 언급한 것보다 훨씬 자세한 내용과 통찰력을 담고 있습니다. 이 포스트는 후발 서비스가 어떻게 허브로 발전할 수 있느냐에 초점이 맞춰져 있습니다.

책에는 마크 그라노베터의 "약한 연결고리의 힘 strength of weak-ties" 이라는 중요한 개념이 나오지만 이 포스트의 관점과는 거리가 있어 언급하지 않았습니다.

이러한 이론적 체계를 갖추었다고 현실에서 당장 실현된다고 할 수는 없습니다. 제 블로그에 방문자나 트위터의 팔로워는 형편 없습니다. ^^;;
저작자 표시
신고

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

   


Posted by 반더빌트
리파지터리 패턴은 그 복합성 때문에 완벽히 이해하고 적용할 수 있기까지 매우 어려운 패턴이라 생각됩니다. 여러번에 걸쳐 이 주제를 포스팅 하는 이유가 바로 그 때문입니다. 여러 문서를 학습하거나 구현에 적용할 때에는 관련된 해결해야 하는 문제들이 등장하게 되죠. 패턴이 잘못 사용되지 않도록 하기위해서는 그 패턴이 가지는 정확한 목적을 이해하는 것이 가장 우선시 되는 것이죠.

리파지터리 패턴은 그 역할과 구현이 명확한 개발언어로 스펙으로 정의 된것이 아닌, 일반언어(plain text)로 정의 되어 있음은 많은 개발자들이 정확한 의미를 파악하기 어려울 수 있습니다. MSDN 에 리파지터리 패턴에 잘 정의된 문서가 있어 [문맥] 과 [목표] 문단을 중심으로 번역 포스트 합니다.

이 문서는 리파지터리 구현시에 발생하는 [데이터 조회 로직]가 리파지터리에 들어가려는 현상이 있는데 이렇게 하는 것이 맞느냐 틀리느냐에 대한 근거 수집 차원에서 접근 했으며, 리파지터리의 세부 성격으로 보았을때 조회 로직 자체도 리파지터리로 부터 분리하는 것이 옳다라는 결론을 얻게 되었습니다.  이 결론이 틀리다 생각되시면 의견을 주시어 생각을 바로 잡을 수 있도록 도와 주시기 바랍니다.

Repository는 저장소 라는 용어로 데이터소스에 대한 관리(CRUD)를 어떻게 추상화 하느냐에 대한 패턴입니다.





문맥:

많은 어플리케이션에서 비즈니스 로직은 데이터베이스, 쉐어포인트, 웹서비스와 같은 데이터저장소의 데이터에 접근합니다. 직접접근은 다음과 같은 부작용을 초래할 수 있습니다.

중복된 코드
프로그래밍 에러가 발생할 높은 잠재성
비즈니스 데이터에 대한 오타
캐싱과 같은 데이터-관계 정책 중심화하기 어려움
외부 의존으로 부터 비즈니스 로직을 분리하기 어려움에 따른 테스트 불가능성


목표:

리파지터리 패턴을 사용하는 것은 다음의 목표를 하나 이상 달성합니다.

당신의 수 많은 코드를 테스트 자동화를 극대화하고, 유닛 테스트를 지원하기 위해 데이터 레이어로 부터 분리 시키기를 원한다.
당신은 여러 곳에서 데이터소스에 접근 하고 있으며, 중앙에서 관리되는 일관성 있는 접근 룰과 로직을 적용기를 원한다.
데이터 소스를 위한  캐싱 caching 전략을 중앙화 centralize 하기를 원한다.
데이터 또는 서비스 엑세스 로직으로 부터 비즈니스 로직을 분리함으로써 코드의 유지관리성 maintainability과 가독성 readability  을 향상 시키기를 원한다.
문제를 런타임이 아닌 컴파일 타임에 구분해 낼 수 있도록 비즈니스 엔터티를 강-타입 strongly typed 으로 사용하기를 원한다.
연결된 데이터와 작업하기를 원한다. 예를들어 필드를 계산하기를 원하거나 복합관계를 강제하거나 엔터티의 데이터 엘리먼트 사이의 비즈니스 룰과 작업하기를 원한다.
복잡한 비즈니스 로직을 단순화 시키기 위해 도메인 모델을 적용하기를 원한다.


솔루션:

비즈니스 로직이 모델에 대해서 동작하도록 함으로써 데이터를 조회하는 로직을 분리하기 위해 리파지터리를 사용한다. 비즈니스 로직은 데이터소스 레이어의 데이터 타입에 대해서 자연스러워야 하며, 데이터소스는 데이터베이스, 쉐어포인트 또는 웹서비스가 될 수 있습니다.

리파지터리는 어플리케이션에서 데이터 소스 레이어와 비즈니스 로직 레이어 사이를 중재 mediates 한다. 리파지터리는 데이터소스에 대해 데이터를 질의하고 데이터 소스와 비즈니스 엔터티 사이의 데이터를 매핑한다. 비즈니스 엔터티의 변경을 데이터 소스에 저장한다. 리파지터리는 데이터소스, 웹서비스에 의존적인 상호작용을 비즈니스 로직으로 부터 분리 시킨다. 데이터와 비즈니스 티어의 분리는 다음의 3가지 잇점을 얻게합니다.

1. 데이터로직 또는 웹서비스 접근 로직을 중앙화 한다.
2. 유닛 테스트를 위한 대안을 제공한다.
3. 어플리케이션 진화 될 수 있는 디자인을 채택할 수 있도록 유연한 아키텍처를 제공한다.

리파지터가 비즈니스 엔터티를 질의하는 두가지 방법이 있습니다. 클라이언트 비즈니스 로직에 대한 질의 오브젝트를 제출 하는 방법, 비즈니스 조건을 정의하는 메소드를 사용하는 방법.  후자의 경우 리파지터리는 클라이언트 행동에 대한 질의를 만듭니다. 리파지터리는 질의를 만족하는 엔터티 집합을 반환합니다. 다음의 다이어 그램은  클라이언트,데이터소스 사이에서 리파지터리의 상호작용을 보여줍니다.


리파지터리의 상호작용 다이어그램.




클라이언트는 지속화를 위해 변경되었거나 새로 생성된 엔터티를 리파지터리에 제출합니다. 더 복잡한 상황에서는 Unit of Work 패턴을 사용할 수도 있습니다. 이 패턴은 연관된 의존을 영속화하기 위해서 수행되어야 하는 여러개의 작업을 어떻게 캡슐화 할 수 있는지를 보여 줍니다. 캡슐화된 아이템은 변경/삭제 행위를 위해 리파지터리에 보내 집니다. 이 가이드는 Unit of Work 패턴의 예제를 포함하지 않습니다. 더 많은 정보를 얻길 원하시면 마틴 파울러의 웹사이트의 Unit of Work 페이지를 방문하세요. 

리파지터리는 서로 다른 도메인에서 데이터와 작업 사이의 다리 bridge 입니다. 일반적인 경우는 데이터베이스 또는 쉐어포인트의 약-타입 의 데이터와 도메인의 강-타입 오브젝트에 매핑하는 것입니다.  한가지 예로 데이터베이스는 질의를 실행하기 위해 IDBCommand 오브젝트를 사용하고 IDataReader 오브젝트를 반환합니다. 리파지터리는 데이터소스에 대한 적절한 질의를 발행합니다. 그 결과를 외부로 노출된 비즈니스 엔터티에 매핑합니다. 리파지터리는 그 표현을 번역하기 위해 Data Mapper 패턴을 사용합니다. 리파지터리는 특정 기술에 대한 의존을 클라이언트로 부터 제거합니다. 예를 들어 클라이언트가 프로덕트 데이터를 조회하기 위해 리파지터리를 호출 한다면, 오로지 카탈로그 리파지터리의 인터페이스만을 사용하면 됩니다. 클라이언트는 프로덕트의 정보를 조회하기 위해 데이터베이스의 SQL로 질의 되는지 쉐어포인트의 CAMIL 이 사용되는지 알 필요가 없습니다. 이런 종류의 의존을 분리 시키는 것은 구현에 있어 유연함을 제공합니다.


구현상세:

쉐어포인트 를 예로 든 설명인데 번역 생략합니다. 코드를 이용한 구현 설명은 아니고 개념 설명입니다.

쉐어포인트 리파지터리의 상호작용 다이어 그램.
 




고려사항:


리파지터리 패턴은 코드의 추상화 정도를 증가시킵니다. 이는 이 패턴에 익숙하지 않은 개발자가 코드를 이해하는데 있어 더욱 어렵게 만들 수 있습니다. 이 패턴을 구현하는 것이 수많은 양의 코드 중복을 감소시키지만  관리해야 하는 클래스 수를 증가 시킵니다. 

리파지터리는 서비스와 리스트에 접근하는 코드를 분리하는데 도움을 줍니다. 고립은 독립적인 서비스처럼 다루기 쉽게 만들어 줍니다. 하지만 전형적으로 리파지터리 그 자체를 테스트하는 것은 매우 어렵습니다. 

멀티 스레드 환경에서 캐싱 데이터를 사용할 때 캐쉬 오브젝트에 대한 동기화도 고려해야 합니다. 종종 ASP.NET 캐쉬와 같은 일반적인 캐쉬는 스레드로 부터 안전하지만, 멀티 스레드 환경에서 작동 되는 오브젝트 그 차제에 대해 스레드로 부터 안전하도록 확인해야 합니다.

여러분이 고부하 시스템에서 데이터를 캐싱 한다면 성능 문제가 부가될 수 있습니다. 데이터소스에 대한 동기화 접근을 고려해 보세요. 이는 데이터에 대한 단일 요청만이 수행되도록 보장합니다. 

### 끝.


참고

리파지터리 패턴 구현에는 마틴파울러의 데이터 매퍼 패턴 (Data Mapper pattern)이 함께 사용됩니다.
리파이터리 패턴 구현에는 마틴파울러의 질의객체 (Query Object) 라는 개념이 사용됩니다.
동적, 재사용가능, Single point decision 을 위해서는 명세서 패턴 (Specification pattern) 이 함께 사용됩니다.
질의의 개별 조건에는 Criteria 라는 개념이 사용됩니다.
저작자 표시
신고

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

   


Posted by 반더빌트
이 포스트의 주제는 이전의 S.O.L.I.D 의  LSP에 이어 의존 역전의 원칙 Dependency Inversion Principle (DIP) 입니다. 포스팅의 동기 또한 LSP의 동기와 같습니다. 개념이해를 돕기  위해 뽑아낸 간략화된 핵심 정의가, 오히려 상세 이해를 방해함을 느꼈습니다. 원칙의 정의만으로는 그 원칙이 의미하는 바를 정확히 이해하기 어렵습니다. 이 포스트는 DIP 원칙이 의미를 설명하는 것입니다. 원칙의 상세를 이해하고, 핵심 정의 구문은 기억을 끄집어 내기위한 실마리로 삼는 것이 가장 좋을 것입니다.

이제 의존 관계 역전의 원칙 DIP :  Dependency Inversion Principle 을 시작하도록 합시다.

먼저 원칙의 이름으로 부터 어떤 원칙인지 힌트를 얻어 보도록 하겠습니다. "의존 관계를 역전 하라는 이야기군, 가만 의존이 뭐지?"


의존이란 무엇인가?

객체-지향 프로그램은 관계를 가지고 있는 객체의 집합 그 자체 입니다. 그 관계를 UML의하면 Multiplicity, Aggregation, Composition, Dependency  4가지의 형태로 일반화 할 수 있습니다. Inheritance 는 종류가 다른 관계라고 생각하도록 하죠.  단순화 하면 객체A 가 객체B를 포함하고 있는데, 그 형태가 위에 언급한 형태 중 하나라는 것이며, 이때 객체A가 객체B를 의존하고 있다 라고 일반화 시켜 말합니다.



의존을 알았으니 추측을 넘어, DIP 의 정의를 알아 보도록 하겠습니다.

A. 고차원의 모듈은 저차원의 모듈에 의존하면 안된다. 이 두 모듈 모두 다른 추상화된 것에 의존 해야 한다.

B. 추상화 된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다.


더 쉽게 말할 수도 있다. 자주 변경되는 컨크리트 클래스에 의존하지 마라. 만약 어떤 클래스의 참조를 가져야 한다면, 참조 대상이 되는 클래스를 추상 클래스로 만들어라. 만약 어떤 함수를 호출해야 한다면, 호출 되는 함수를 추상 함수로 만들어라.

일반적으로, 추상 클래스와 인터페이스는 자신에게서 유도된 구체적인 클래스 보다 훨씬 덜 변한다.

Java 프로그래머를 위한 UML 실전에서는 이것만 쓴다!. 로버트 C.마틴 지음 / 이용원, 정지호 옮김, 인사이트 출판사, p140, 141 


음... 가능한 추상 클래스나 인터페이스를 참조 함으로써 의존을 좀더 느슨하게 만들어라는 얘기군. 알겠어, 앞으로는 최대한 추상 클래스를 만들고, 인터페이스를 끄집어 내서 DIP 를 지키는 개발자가 되겠어!  근데 관계의 역전은 뭘 의미하는 거지? 위의 정의에 역전이라는 말은 언급되지도 안잖아, 에이 머리 아파~ 이 정도만 이해하면 되겠지.

관계의 역전의 뜻에 대해 의문점을 남긴채 찜찜한 상태로 공부를 마무리 하게 됩니다.




자! 이제부터 시작입니다. 역전은 뭘 말하는 걸까요?. DIP 의 성배를 찾으러 갑시다.


DIP를 제시한 로버트 C. 마틴보다 더 잘 설명할 능력이 부족하기도 하고, 지금까지 읽은 문서중 가장 잘 설명하고 있다고 생각하는 마틴의 DIP 문서의 요약 및 해석으로 글을 쓰도록 하겠습니다.



의존 관계 역전의 원칙 The Dependency Inversion Principle 


이전 글에서 Liskov Substitution Principle LSP 에 대해 이야기 했습니다. LSP는 C++ 에서 상속을 사용할 때 일반적인 가이드를 제공하는 것입니다.  파생되는 클래스는 기반 클래스의 가상 멤버 함수의 약속이 파생 클래스에서도 제공되어 져야 한다는 것이고, 이를 위반할 경우 객체의 적절한 작동을 보장하기 위해서는  실제 객체의 타입을 확인해야 절차가 유발되며, 이 행위는 OCP를 위반하게 된다 였습니다.

우리는 OCP 와 LSP를 논했고, 이 원칙들이 엄격하게 지켜져서 얻어지는 구조를 일반화 시킬수가 있는데, 이 구조를 "의존 관계 역전의 원칙 The Dependency Inversion Principle" 이라고 이름 붙이겠습니다.


소프트웨어에 뭐가 잘못 되어가고 있는가? What goes wrong with software?

우리 대부분은 "나쁜 디자인"의 소프트웨어 조각으로 일을 해본 유쾌하지 않은 경험을 가지고 있습니다. 우리중 일부는 그 "나쁜 디자인"의 주인이 자기 자신이었다는 것을 발견하는 더욱 나쁜 경험을 가지고 있을지도 모릅니다. 도대체 디자인을 나쁘게 만드는 것은 무엇일까요?


"나쁜 디자인"의 정의 The Definition of a "Bad Design"

여러분은 다른 사람들에게 자신이 특별히 자랑스러워하는 소프트웨어 디자인을  발표해 본적이 있습니까?  그때 "왜 그런 방법으로 하셨나요?" 라는 말을 들어 본적이 있습니까? 이건 저에게 일어난 일이고, 또 많은 엔지니어들에도 벌이지고 있는 일입니다. 디자인에 동의하지 않는 엔니지어는 "나쁜 디자인"에 대한 정의에 대해 나와 동일한 기준을 가지고 있지 않았습니다. 엔지니어들에게서 발견한 가장 일반적인 기준은 "나는 그런 방법으로 하지 않아" 였습니다.

하지만, 모든 엔지니어들이 동의하는 나쁜 디자인에 대한 기준이 있었으니 다음과 같습니다.

1. 모든 변경마다 많은 다른 부분에 영향을 미쳐 변경 자체가 어렵다. (Rigidity)
2. 변경 작업을 할때 예상치 못한 다른 부분이 망가진다. (Fragility)
3. 현재의 어플리케이션에서 분리할 수 없기 때문에 다른 어플리케이션에서 재사용 하기가 매우 어렵다. (Immobility)

그 외에도 유연성, 견고성 과 같은 설명하기 어려운 요소들이 있었습니다. 따라서 위의 세가지 항목을 "좋고, 나쁨의" 기준으로 삼기로 결정 했습니다.


 "나쁜 디자인"의 원인 The Cause of "Bad Design"

무엇이 디자인을 딱딱하고 rigid, 깨지기 쉽고 fragile, 이동할 수 없게 immobile 만드는 것일까? 그건 모듈 디자인의 상호의존 interdependence 때문입니다. 

변경이 어려운 rigidity 디자인 원인은 강하게 상호의존하고 있는 부분의 단일 변경이 그가 의존하는 모듈에게 연쇄변경을 요구하기 때문입니다. 이러한 일련의 변경이 확장되면 설계자와 유지관리자는 그를 예측, 추정하지 못합니다. 이는 변경 승인을 주저하게 만들고, 디자인은 공식적으로 경화 되었다라고 선언 됩니다.

깨지기 쉬움 fragility 이란 단일 변경의 영향으로 매우 많은 부분이 고장남을 뜻합니다. 개념적으로 관련이 없는 영역에서도 종종 새로운 문제가 발생합니다. 이런 깨지기 쉬움은 설계와 관리조직의 신뢰성을 급격히  떨어트립니다. 사용자와 관리자는 상품의 품질을 예상할 수 없게 됩니다. 한 부분의 간단한 변경이 그와 명백히 관련이 없는 부분에서 실패를 만들어 냅니다. 이런 문제를 해결하는 일 자체는 더욱 문제로 부상합니다. 유지보수 프로세스가 자신의 꼬리를 쫒는 개와 닮았거든요.

이동 불가능한 immobile 디자인이란 이동을 희망하는 어떠한 부분이 원치 않는 다른 부분의 상세를 강력하게 의존하고  있는 것을 말합니다. 디자이너는 새로운 어플리이션에 재사용 될 수 있는지 기존 어플리케이션을 조사 합니다. 이동시키고 싶은 부분을 분리하기 위해 매우 많은 부분으로 부터 분리해야 한다면, 대부분 그러한 디자인은 새로 개발하는 것보다 비용이 높기 때문에 재사용될 수 없습니다.



의존 관계 역전의 원칙 The Dependency Inversion Principle


A. 하이 레벨 모듈은 로우레벨 모듈에 의존해서는 안된다. 둘다 추상에 의존해야 한다.

B. 추상은 상세를 의존해서는 안된다. 상세는 추상을 의존해야 한다.

A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.

B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.


Copy 모듈은 Keyboard 와 Printer 디바이스의 상세를 사용합니다. - 구조적 디자인의 예



내가 왜 "역전 inversion" 이라는 단어를 사용했는지 의문이 생길 겁니다. 바로 구조적 분석, 구조적 디자인으로 대표되는 전통적인 개발 방법 때문입니다.  하이 레벨 모듈이 로우 레벨 모듈을 의존하는 구조, 추상이 상세를 의존하는 구조적 디자인 말이죠. 전통적 설계의 목적은 하이 레벨 모듈이 로우 레벨 모듈을 어떻게 호출할 것인지의 서브프로그램 구조를 정의하는 것 입니다.  [그림 1 ]은 구조적 디자인 구조의 좋은 예 입니다. 그래서 잘 설계된 객체-지향 프로그램의 의존 구조는 전통적 절차적 메소드의 결과로 만들어지는 의존 구조를 "역전 invert"한 형태 입니다.


 하이 레벨 모듈이 로우 레벨 모듈을 의존하고 있는 경우를 고려해 봅시다. 하이 레벨 모듈은 어플리케이션의 중요한 정책 결정과 비즈니스 모델을 담고 있습니다. 모델은 어플리케이션의 정체성을 담고 있죠. 이 모듈들이 로우 레벨 모듈을 의존하고 있고, 로우 레벨 모듈에서 발생하는 변경은 하이 레벨 모듈들에 직접적인 영향을 미치며 마침내 변경을 강요합니다.


이건 완전히 말이 안되는 상황입니다. 변경을 강요해야 하는 것은 하이 레벨 모듈이어야 하며, 하이 레벨 모듈이 로우 레벨의 상급자가 되어야 합니다. 어떤 상황이건 하이 레벨 모듈이 로우 레벨 모듈을 의존 해서는 안됩니다.

더 나아가, 하이 레벨 모듈은 재사용 가능해야 합니다. 우리는 이미 서브루틴 라이브러리와 같은 형태로 로우 레벨 모듈을 재사용하는데 정통해 있습니다. 하이 레벨 모듈이 로우 레벨 모듈을 의존하고 있으면, 하이 레벨 모듈은 문맥이 다른 곳에 재사용되기 어려워 집니다. 그러나, 하이 레벨 모듈이 로우 레벨 모듈에 독립적 이라면, 하이 레벨 모듈은 매우 간단히 재사용 될 수 있습니다. 이것이 바로 프레임워크 디자인의 핵심 입니다.
 



레이어링 Layering

Booch 에 따르면 " 잘 구조화된 객체-지향 아키텍처는 명확하게 정의된 레이어를 가지고 있다. 개개의 레이어는 잘-정의 되고, 통제되는 인터페이스를 통해 응집성 있는 서비스의 집합을 제공한다." 이 문장을 순진하게 해석한 디자이너가 만들어내는 구조는 [그림 3] 과 같을 것입니다. 이 다이어그램에서 하이 레벨 정책 클래스는 로워 레벨 메커니즘을 사용합니다. 이는 상세 레벨의 유틸리티 클래스를 사용하는 형태로 변합니다.  정책 레이어가 변경에 민감해서 유틸리티 레이어에 변경이 전파되는 교활한 특성을 보이더라도 괜찮지만, 의존은 전이적 transitive 입니다. 정책 레이어는 유틸리티 레이어를 의존하는 그 어떤 것(여기서는 메커니즘 레이어)에 의존하고 있습니다.  그래서 정책 레이어는 유틸리티 레이어를 의존하는 불행한 형태가 됩니다.




[그림 4] 는 보다 좋은 모델입니다. 각각의 로워 레벨 레이어들이 추상 클래스로 표현되고 있습니다. 실체 레이어는 추상레이어로 부터 파생 됩니다. 각각의 하이 레벨 레이어는 추상 인터페이스를 이용해 다음 단계의 로워 레벨 레이어를 사용합니다. 따라서 레이어들은 다른 레이어들을 의존하는 대신에 추상 클래스를 의존합니다. 정책 레이어의 의존이 전이되면 유틸리티 레이어만을 망가뜨리는 것이 아닙니다. 정책 레이어가  직접적으로 의존하고 있는 메커니즘 레이어 조차도 망가뜨립니다. 


이 모델을 사용하면 정책 레이어는 메커니즘 레이어나 유틸리티 레이어의 변경으로 부터 영향을 받지   않습니다. 더 나아가,  정책 레이어는 메커니즘 레이어의 인터페이스를 준수하는 로워 레벨 모듈의 정의를 가진 다른 문맥에서 재사용 될 수 있습니다. 의존을 역전시킴으로써 우리는 좀더 유연하고, 견고하고, 이동성 있는 구조를 만들 수 있습니다.


단순한 예제 Simple Example

의존 역전은 한 클래스가 다른 클래스에 메시지를 보낼때 적용할 수 있습니다. button 객체와 lamp 객체를 생각해 보세요. 

button 객체는 외부 환경을 감지하고, 사용자가 버튼을 눌렀는지 여부를 결정합니다. lamp 객체는 외부 환경으로 부터 영향을 받습니다. TurnOn 메시지를 받으면 불을 밝히고, TurnOff 메시지를 받으면 불을 끕니다.

 button 객체가 lamp 객체를 제어하는 시스템을 어떻게 설계할까요?

Figure 5는 이를 구현한 순진한 모델입니다. 

button 객체는 lamp 객체에게 TurnOn, TurnOff 메시지를 보냅니다.  button 클래스는 lamp 클래스 인스턴스와 관계를 가지기 위해서 lamp를 "포함" 합니다. 

 


위 모델의 구현은 아래와 같습니다.

Figure 5 는 의존 관계 역전의 원칙을 위반합니다. 어플리케이션의 하이-레벨 정책이 로우-레벨 모듈과 분리되지 않았습니다. : 추상과 상세가 분리되지 않았다는 말입니다. 분리하지 않으면 하이-레벨 정책이 자동적으로 로우-레벨 모듈을 의존합니다. 추상이 상세를 자동적으로 의존하는 것입니다.


[전통적인 button/lamp model 구현 코드]

namespace TradButtonModel
{
    class Lamp{
        public void TurnOn()
        {
            Console.WriteLine("Trad Lamp 램프를 켭니다.");
        }
        public void TurnOff()
        {
            Console.WriteLine("Trad Lamp 램프를 끕니다.");
        }
    }

    class Button {
        private Lamp itsLamp;

        public Button(Lamp lamp)
        {
            this.itsLamp = lamp;
        }

        public void Detect()
        {
            bool buttonOn = GetPhysicalState();
            if (buttonOn)
            {
                itsLamp.TurnOn();
            }
            else
            {
                itsLamp.TurnOff();
            }
        }

        private bool GetPhysicalState()
        {
            bool isPressed = false;
            //물리적 장치가 눌려저 있으면 true 를 반환
            return isPressed;
        }
    }



}


의존 하는 추상 찾기 Finding the Underlying Abstraction

하이 레벨 정책이란 무엇일까요? 이것은 어플리케이션이 의존하는 추상입니다. 상세가 변한다 해도 변경되지 않는 것을 의미하죠. Button/Lamp 예제에서 의존하는 추상이란 사용자의 on/off 제스처를 감지하고 타겟 객체에게 전달하는 것을 의미합니다. 말도 안됩니다!, 타겟 객체는 또 뭐죠? 타겟 객체는 추상에 영향을 주지 않는 상세 입니다.

의존 관계 역전의 원칙을 적용하려면 문제의 상세로부터 추상을 분리해야 합니다. 그런 다음, 추상이 상세를 의존하는 Figure 5 를 Figure 6 와 같이 변경할 수 있습니다.



Figure 6, 우리는 button 클래스의 상세 구현으로 부터 추상을 격리하였습니다. 


상위 레벨 정책이 완전히 추상 button 클래스만을 가지고 있음을 주목하세요. Button 클래스는 사용자   제스처를 감지하는 물리적 메커니즘을 전혀 모릅니다. 그리고, Lamp 에 대해서도 전혀 모릅니다. 

이 상세들은 추상을 구현한 ButtonImplementation, Lamp 클래스에 의해 추상으로 부터 격리됩니다. 


[DIP를 적용한 button/lamp model 구현 코드] 

namespace InvertedButtonModel
{
    abstract class ButtonClient
    {
        public abstract void TurnOn();
        public abstract void TurnOff();
    }


    class Lamp : ButtonClient
    {
        public override void TurnOn()
        {
            Console.WriteLine("Lamp 램프를 켭니다.");
        }

        public override void TurnOff()
        {
            Console.WriteLine("Lamp 램프를 끕니다.");
        }
    }


    abstract class Button
    {
        private ButtonClient buttonClient;

        public Button(ButtonClient client)
        {
            this.buttonClient = client;
        }
        public void Detect()
        {
            bool buttonOn = GetState();
            if (buttonOn)
            {
                buttonClient.TurnOn();
            }
            else
            {
                buttonClient.TurnOff();
            }
        }
        public abstract bool GetState();


    }

    class ButtonImplementation : Button
    {
        public ButtonImplementation(ButtonClient client):base(client){}

        public override bool GetState(){
            bool isPressed = false;
            //물리적 장치를 감지하여 반환
            return isPressed;
        }

    }
}


결론 Conclusion

의존 관계 역전의 원칙은 객체-지향 기술이 주장하는 장점의 근원입니다. 좋은 어플리케이션은 재사용 가능한 프레임워크를 만들길 요구합니다. 변경에 강한 코드의 구조를 만드는 것은 매우 중요 합니다. 그리고, 추상과 상세를 서로 격리시키기 때문에 코드 관리는 더욱 쉬워 집니다.

의존 관계의 역전 dependency inversion  이란 구조적 디자인에서 발생하던 로워 레벨 모듈의 변경이 하이 레벨 모듈의 변경을 요구는 위계의 관계를 끊자 라는 의미로 쓰여진 역전입니다. 실제의 사용 관계는 바뀌지 않으며, 추상을 매개로 메시지를 주고 받음으로써 관계를 최대한 느슨하게 만들어야 한다는 원칙입니다.

구현 관점에서 추상 클래스, 인터페이스를 사용하는 것부터 시작하여, 아키텍처 관점까지 확장할 수 있습니다. 예를 들어,  데이터베이스를 연결하는 레이어로 OLEDB를 사용한다면, OLEDB의 인터페이스를 제공하는 물리 DB가  MSSQL 에서 ORACLE 로의 변경되더라도 그 변경이 상위로 전파되지 않는다 라고 이해 할 수 있습니다.
 


하이 레벨과 로워레벨 사이에 추상을 삽입함으로써 로워 레벨의 변경이 하이 레벨로 전파되는 것을 막는다.



끝으로, 


"A. 하이 레벨 모듈은 로우레벨 모듈에 의존해서는 안된다. 둘다 추상에 의존해야 한다." 라는 정의는  "A. 하이 레벨 모듈은 로우레벨 모듈에 의존해서는 안된다 의존 하되, 둘다 추상에 의존해야 한다." 로 해석하는 것이 혼란을 피하는 것이라 생각됩니다.


### 끝.


참고자료 : 
로버트 C. 마틴 The Dependency Inversion Principle.pdf [Martin96] Robert C. Martin, Engineering Notebook, C++ Report
 


책 : java 프로그래머를 위한 UML 실전에서는 이것만 쓴다!. 로버트 C.마틴 지음 / 이용원, 정지호 옮김, 인사이트 출판사, p140,p141 

 




저작자 표시
신고

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

   


Posted by 반더빌트
리스코프 치환 원칙 LSP Liskov Substitution Principle 은 객체-디자인의 S.O.L.I.D 5원칙 중의 가장 이해하기 어려운 원칙이라고 할 수 있습니다. 다른 원칙들은 이름에서 힌트를 얻을 수 있는데 LSP만은 이름으로 부터 힌트를 얻을 수가 없습니다.

이 글을 읽고 있다면 이미 S.O.L.I.D 를 공부해 본적이 있을 겁니다. 독일어나 러시아어 이름 같은  리스코프 라는 어감은 이 원칙을 이해하면 뭔가 대단한 것을 알고 있다는 느낌이 들것도 같습니다. 이해하기 위해 정의를 수십번 읽어 봐도 대부분의 원칙이란걸 대할때 그러하듯 "그래서 이거 가지고 뭘 하라는 건데" 라는 자조섞인 답변이 들리기도 하죠.  

수 많은 문서와 책,  인터넷의 블로그 글들은 리스코프 치환 (교체) 원칙에 대해 다음과 같이 기술하고 있습니다.

서브타입은 언제나 자신이 기반타입 (base type)으로 교체할 수 있어야 한다.

유도된 클래스의 메소드를 퇴화시키거나 불법으로 만드는 일을 피하라. 기반 클래스의 사용자는 그 기반 클래스에서 유도된 클래스에 대해 아무것도 알 필요가 없어야 한다.
java 프로그래머를 위한 UML 실전에서는 이것만 쓴다!. 로버트 C.마틴 지음 / 이용원, 정지호 옮김, 인사이트 출판사, p137,p144  에 있는 정의 입니다. 


"서브타입을 기반타입으로 교체할 수 있어야 한다고?  음... 다음 말은 무슨 뜻인지 잘 모르겠고, 상속 받고, 메소드를 빠짐없이 구현하면 되잖아, 기반 타입에 파생 타입을 대입할 수도 있고, 봐~~ 컴파일도 되고, 작동하잖아, LSP 다 됐어!" 라고 많은 개발자들이 넘어 갔지만, 화장실 갔다가 마무리를 짖지 않은 것 처럼 뭔가 찜찜한 기분이 들었다면, 아래의 내용을 한줄도 빠짐 없이 읽어주시길 바랍니다. 



리스코프 치환 원칙이란?

1998년 Barbara Liskov (MIT 컴퓨터 사이언스 교수, 2008 튜링상 수상) 가 제안한 원칙으로 :

타입 S의 객체 o1 과 타입 T의 인스턴스 o2가 있을 때, 어떤 프로그램에서 타입 T의 객체로 P가 사용된다고 하자. S가 T의 서브타입이라면 P에 대입된 o1이 o2로 치환 된다고 해도 P의 행위는 바뀌지 않는다. 

Barbara Liskov wrote LSP in 1988:
What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T." - BarbaraLiskov, Data Abstraction and Hierarchy, SIGPLAN Notices, 23,5 (May, 1988).

로버트 C. 마틴의 LSP 정의  :

참조되는 기반클래스의 함수는 파생클래스 객체의 상세를 알지 않고서도 사용될 수 있어야 한다. 

FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE
CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES
WITHOUT KNOWING IT.


리스코프 치환 원칙이 왜 중요한가?

1. 이 원칙을 지키지 않으면 클래스 계층이 지저분하게 될 꺼에요. 서브클래스 인스턴스를 파라미터로 전달 했을 때 메소드가 이상하게 작동 할 수 있습니다.


2. 슈퍼클래스에 대해 작성된 단위테스트가 서브클래스에 대해서는 작동되지 않을 것입니다.




로버트 C. 마틴 Liskov Substitution Principle

로버트 마틴은 LSP를 다음과 같이 소개 하고 있습니다.
개방 폐쇄 원칙 OCP 은 관리가능하고 maintainable, 재사용 reusable 가능한 코드를 만드는 기반이다. 잘 설계된 코드는 기존 코드의 변경 없이 확장 가능하다. OCP 를 가능케 하는 중요 메커니즘은 추상화와 다형성이다. 추상화와 다형성을 가능케 하는 키 메커니즘이 상속이다. 추상 기반 클래스의 순수 가상 함수로 부터 클래스를 상속 파생 시킴으로써 추상화된 다형 인터페이스를 만들어 낼 수 있다.

상속을 적용함에 있어서 디자인 룰은 무엇일까? 최고의 상속 구조의 특성은 무엇일까? 계층 구조를 만듦에 있어 OCP를 위반하게 하는 덧은 무엇일까?


LSP가 바로 상속의 룰 이며, 최고의 상속 구조 특성을 갖추게 하며, OCP를 위반하지 않도록 인도하는 원칙 이라고 말하고 있습니다.  



정사각형과 사각형, 그리고 심각한 위반, Square and Rectangle, a More Subtle Violation.

LSP를 위반하는 사례를 살펴봅시다. 로버트 C. 마틴의 LSP 문서의 사례 입니다. C++ 로 작성된 예제를 C#으로 바꾸었습니다.

아래와 같이 정의된 Rectangle 클래스를 사용하는 어플리케이션이 있습니다.
 public class Rectangle
    {
        protected double itsWidth;
        protected double itsHeight;
    
        public void SetWidth(double w)
        {
            this.itsWidth = w;
        }

        public void SetHeight(double h)
        {
            this.itsHeight = h;
        }

        public double GetWidth()
        {
            return this.itsWidth;
        }
        
        public double GetHeight()
        {
            return this.itsHeight;
        }
    }


이 어플리케이션은 많은 사이트에 설치 되었고, 매우 잘 작동하고 있다고 상상해 봅시다. 매우 성공한 소프트웨어라고 할 수 있습니다. 어느날 사용자가 정사각형 square 요소를 추가해 달라고 요구해 왔습니다. 상속은 ISA 관계이고, 새로 추가할 객체가 기존 객체의 ISA 관계를 만족 시킨다면, 새로운 객체는 기존 객체로 부터 파생 시킬수 있습니다.


명확하게 정사각형은 사각형의 일반적인 의도를 만족 시켰습니다. ISA 관계가 성립되기 때문에, Square 클래스를 Rectangle 클래스로 부터 파생시키는 것은 논리적으로 완벽합니다.

ISA 관계를 사용하는 것은 객체-지향의 기반 기법을 사용하는 것으로 사료됩니다. Square 클래스를 Rectangle로 부터 파생시켜면 문제가 있을 수 있지만, 이런 문제는 실제로 코드에 적용해 보지 않고서는 알 수 없습니다.
 

Rectangle, Square 상속 다이어 그램



우리가  눈치 챈 첫번째 문제는 Square는 itsHeight, itsWidth 의 두개의 멤버 변수가 필요하지 않지만 상속된다는 것 입니다. 이건 명확하게 쓸모 없는 것이고, CAD와 같은 프로그램에서 수백 추천개의 정사각형을 그리게 된다면 문제가 발생할 건 확실합니다.


메모리 효율을 문제 삼지 않는다고 가정합시다. 다른 문제가 있을 까요?  이런! Square 는 SetWidth 와 SetHeight 함수도 상속 받습니다. Square는 가로와 세로가 같아야 하기 때문에 설계의 문제가 발생한 겁니다. 이 문제를 해결하기 위해 아래와 같이 오버라이드 override 할 수 있습니다.
 


  public class Square : Rectangle
    {
        /// 
        /// 정사각형이기 때문에 너비를 할당하더라도 높이도 같이 할당해 줌.
        /// 
        /// The w.
        /// 기반 타입의 SetWidth를 호출 하면 
        /// 기반 타입의 메소드가 호출 됨.
        /// 
        public new void SetWidth(double w)
        {
            base.itsWidth = w;
            base.itsHeight = w;
        }

        /// 
        /// 정사각형이기 때문에 높이를 할당하더라도 너비도 같이 할당해 줌.
        /// 
        /// The h.
        /// 
        /// 
        public new void SetHeight(double h)
        {
            base.itsWidth = h;
            base.itsHeight = h;
        }
    }



누군가 Square 객체에 가로값을 적용한다 해도 높이가 자동으로 적용되고, 정사각형이라는 논리를 위반하지 않습니다.



하지만 아래의 함수를 고려해 보면 문제가 있습니다.


Square 객체의 Height가 변경되지 않을 것이기 때문에 지금까지 쌓아 놓은 것은 무너 집니다. Rectangle의 SetWitdth , SetHeight가 virtual 로 선언 되지 않았기 때문에 발생한 문제 입니다.

 


     /// 
        /// Fs the specified rect.
        /// 
        /// The rect.
        /// 
        /// 
        static void f(Rectangle rect)
        {
            rect.SetWidth(32);
        }




진짜 문제 The Real Problem

이제 우리는 Rectangle, Square 두개의 클래스를 가지고 있고, Square 객체로 어떠한 일을 하더라도 문제가 없어 보입니다. Square 는 수학적으로도 일관성이 있고, Rectangle 도 문제가 없습니다. 

아래의 함수 g를 생각해 봅시다.
 


     /// 
        /// Gs the specified rect.
        /// 
        /// The rect.
        /// 
        /// 
        static void g(Rectangle rect)
        {
            rect.SetWidth(5);
            rect.SetHeight(4);

            //파라미터로 Square 타입이 전달되면 Assertion 에러를 발생 시킵니다.
            System.Diagnostics.Debug.Assert(rect.GetWidth() * rect.GetHeight() == 20);
        }



Rectangle의 메소드를 실행시키는 함수 입니다. Rectangle 객체에 대해서 올바르게 동작하고, Square 객체가 전달 될 때는 assertion 에러를 발생하도록 한겁니다.

함수 g를 만든 프로그래머는 매우 합리적인 가정을 했습니다. 다른 프로그래머가 함수 g에 Square 객체를 전달하면 assertion  에러를 보고 받을 테니깐요.

하지만, 부모인 Rectangle 객체에서 작동하는 행위가 Square 객체에 대해서 작동하지 않는다는 것은 LSP를 위반하는 것이고, 파생클래스 행위의 내부 상세를 안다는 것은 OCP open-close principle 또한 위반 하는 것입니다. 


public class Rectangle
    {
        protected double itsWidth;
        protected double itsHeight;
    
        public virtual void SetWidth(double w)
        {
            this.itsWidth = w;
        }

        public virtual void SetHeight(double h)
        {
            this.itsHeight = h;
        }

        public double GetHeight()
        {
            return this.itsHeight;
        }

        public double GetWidth()
        {
            return this.itsWidth;
        }
    }

    public class Square : Rectangle
    {
        /// 
        /// 정사각형이기 때문에 너비를 할당하더라도 높이도 같이 할당해 줌.
        /// 
        /// The w.
        /// 기반 타입의 SetWidth 메소드를 호출하더라도
        ///  파생 타입의 메소드가 호출됨.
        /// 
        public override void SetWidth(double w)
        {
            base.itsWidth = w;
            base.itsHeight = w;
        }

        /// 
        /// 정사각형이기 때문에 높이를 할당하더라도 너비도 같이 할당해 줌.
        /// 
        /// The h.
        /// 
        /// 
        public override void SetHeight(double h)
        {
            base.itsWidth = h;
            base.itsHeight = h;
        }
    }



무엇이 잘 못 되었는가? What Went Wrong (W3)


무슨 일이 일어 난거야? Rectangle과 Square라는 논리적인 모델이 뭐가 잘 못 된거지?  무엇보다도 정사각형은 사각형이다. ISA 관계도 만족 시키잖아!!!.


정사각형은 사각형이지만, Square 객체는 Rectangle 객체가 아닙니다. 왜 일까?  Square 객체의 행위는 Rectangle 객체의 행위와 일관성이 없습니다. 행위적으로 Square 는 Rectangle 이 아니고, 행위는 소프트웨어의 거의 모든 것입니다.

LSP는 OOD의 행위적 ISA 관계를 명확하게 해줍니다. : 본질적인 행위보다 클라이언트가 의존하는 비본질적인 행위를 더욱 중요시 합니다. 함수 g를 작성한 프로그래머는 사각형의 너비와 높이는 독립적으로 설정될 수 있다는 사실에 근거하였습니다. 두 변수의 독립성은 다른 프로그래머들이 의존하는 외적으로 공개적인 행위 입니다. 



계약에 의해 설계하기 Design by Contract

Bertrand Meyer (Eiffel programming language 창시자, Object Oriented Software Construction 저자) 에 따르면  LSP와 Design by Contract 는 매우 깊은 관계가 있습니다. 클래스 메소드의 선행 조건 precondition 과 후행조건 postcondition 에 이런 구조를 적용한다면, 메소드를 실행하기 위해선 선행조건이 만족해야 하고, 그렇다면, 메소드는 후행조건이 참일 것이라는 것을 보장 할 수 있습니다.


Meyer 는 파생클래스의 선행조건과 후행조건에 대한 규칙을 정의 했습니다.: 선행조건이 약하고, 후행조건이 강할 때에만 루틴을 재정의가  가능하다.

...when redefining a routine [in a derivative], you may only replace its
precondition by a weaker one, and its postcondition by a stronger one.

Object Oriented Software Construction, Bertrand Meyer, Prentice Hall, 1988, p256


다시말해서, 기반클래스의 인터페이스를 사용할 때, 사용자는 오로지 기반클래스의 선행조건과 후행조건만을 알고 있기 때문에, 파생 객체는 기반클래스 보다 더 강한 요구사항을 사용자가 지키라고 강요할 수 없다.  또한 파생 클래스는 기반 클래스의 모든 후행 조건을 만족 시켜야만 한다.  이것이 파생클래스의 행위에 대한 결과가 기반 클래스의 제약 사항을 위반하지 않는 것이고, 기반 클래스의 사용자는 파생클래스의 결과물에 혼란스럽지 않게 될 것이다.



맺음말

오리처럼 생겼고, 오리 울음 소리를 내지만 배터리가 필요하다면 - 아마도 잘못된 추상화를 한것이다. LSP



객체-지향 디자인에서 상속에 대한 룰을 어느정도로 엄격하게 지키냐는 가치판단의 문제 입니다. LSP 원칙은 상속에 대한 룰을 가장 엄격함을 적용하는 것이라 할 수 있습니다. 상속을 무분별하게 적용하고 있는 현재의 관행은 상속을 적용함에 있어 더욱 엄격해야 한다는 것이 커뮤니티의 주류 의견되고 있습니다. 객체-지향 디자인이 이루고자 하는 목적이 작동하기만 하는 소프트웨어가 아닌 관리가능한, 재사용가능한, 신뢰할 수 있는, 유연한 소프트웨어의 개발이기 때문입니다.

### 끝.

참고자료 :
로버트 C. 마틴 Liskov Substitution Principle.pdf [Martin96] Robert C. Martin, Engineering Notebook, C++ Report, Nov-Dec, 1996.
 

리스코프 치환 원칙 잘 정리된 wiki 문서


책 : java 프로그래머를 위한 UML 실전에서는 이것만 쓴다!. 로버트 C.마틴 지음 / 이용원, 정지호 옮김, 인사이트 출판사, p137,p144 
저작자 표시
신고

이 글을 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 반더빌트
객체-지향 디자인은 위기에 빠진 소프트웨어를 구할 수 있는 메시아 처럼 받아들여 집니다. 진짜 메시아처럼 그 정체를 정확히 아는 사람도 얼마 없다라는 것도 닮았군요. 소프트웨어 개발의 어려운 점은 개념 자체 이해가 어렵다는 것이 크다고 생각합니다. 이 블로그에서 개념에 집착하는 이유가 거기에 있습니다. 

객체-지향 언어, 객체-지향 디자인, 객체-지향 프로그래밍 이란 용어는 아마도 가장 이해하기 어려운념이며, 구원자를 앞에 두고 알아보지 못하고 있는 것 같은 상황입니다. 객체-지향을 설명하는 수많은 책과 글이 있음에도, 정확히 이해하지 못하는 상황에 빠져 있다는 것은 무엇인가 문제가 있음을 증명하고 있습니다.

주된 이유는 객체-지향이라는 개념이 모호한 용어의 조합으로 설명하기 때문이며, 사람에게 익숙한것은 객체-지향이 아니라 절차적 프로그래밍이라는 생각이 들기도 합니다. 기본적으로 사람의 사고 체계의 기본 단위는 절차적입니다. 한 발짝 뒤로 물러 서서 보면 절차를 감싸고 있는 객체가 있음에도 희안하게 그게 잘 보이지 않습니다.

객체-지향을 좀더 쉽고 정확하게 설명하자, 그리고, 객체-지향이 만들어 내는 현상에 현혹되지 말고 핵심을 설명하자는 것이 객체-지향을 수차례에 걸쳐 포스팅 하는 이유입니다.

이전 포스트 : 객체-지향 프로그래밍 이란 무엇인가 (OOP)? 는 객체-지향의 각론에 대한 이야기입니다.  

간단한 프로그램을 작성한다면 구조적 프로그래밍으로도 충분합니다. 뉴튼의 만유인력의 법칙 만으로도 태양계 행성들과 자연계의 움직임을 근사값에 가깝게 설명할 수 있는 것 처럼 말이죠. 하지만, 복잡한 프로그램을 관리가능하게 작성하려면 아인슈타인의 상대성이론을 적용해야 합니다. 그게 문제 인거죠. 만유인력의 법칙이 구조적 프로그래밍 이라면 상대성 이론이 객체-지향 프로그래밍 입니다.

객체-지향을 설명하는 많은 글들 중에 저의 생각과 일치하는 책이 있어, 객체-지향 프로그래밍 개념에한 단락을 번역 포스트 합니다. 98년에 출판된 책인데 번역서는 없더군요.  바로 Stephen Gilbert 와 Bill McCarty의 저서 Object-Oriented Design in Java, published by Sams, 1998 입니다. 


객체-지향 프로그램이란 상호작용 하는 객체의 집합 입니다.




객체-지향을 정확하기 이해하기 위한 5개의 개념 정의와 중요한 관점에 대해 정리해 보자면, 

1. 객체란 무엇인가?
데이터, 행위, 아이덴티티를 가지고 있는 것.

2. 클래스란 무엇인가? 그리고, 객체와 클래스의 차이는 무엇인가?
클래스란 객체를 생성하는 청사진 이다.

3. 캡슐화란 무엇인가?
행위와 상태를 포장하고 외부에 노출할 것과 감출 것을 결정하는 것. 외부에 노출되는 모든 것을 인터페이스 interface 라고 할 수 있다.

4. 상속이란 무엇인가?
가족 관계를 나타내는 것이다. 자식은 부모의 자산을 이용할 수 있으며, 코드 재사용이 첫번째 달성되는 것이며,  상속은 클래스의 계층 구조를 표현 할 수 있게 되는데, 계층 구조는 그 구조 자체만으로 수 많은 정보를 포함 할 수 있다. 계층 구조를 표현하는 것이 두번째 핵심이다.

많은 문서에서 상속을 코드 재사용의 관점에서 기술하고 있지만, 상속의 핵심은 계층구조를 표현 하는 것입니다. 재사용만을 목적으로 상속을 이용한다면 가장 강력하게 커플링된 재사용방법을 쓰고 있는 것입니다.  공통 라이브러리를 사용하는 것이 훨씬 좋은 방법이죠. 계층구조를 포함할 목적이 아니라면 상속을 사용해서는 안되며, 코드 재사용 보다 계층구조의 표현이 상속에서 더욱 중요한 개념이다 라는 것입니다. 

 
5. 다형성이란 무엇인가? 
캡슐화, 상속과 함께 동작 함으로써 객체-지향 프로그램의 흐름 제어를 단순화 하는 것 입니다.

다형성을 설명하는 많은 문서는  
서브클래스의 오버라이드된 메소드가  다른 작업을 할 수 있어서 다양한 형태라는 다형성이라고 설명하는데 포커스가 맞춰 있지만 다형성의 핵심은 흐름 제어 Flow of Control 를 객체로 처리 하도록 단순화 하는 것 입니다.



아래는 
Stephen Gilbert 와 Bill McCarty의 저서 Object-Oriented Design in Java, 1998 의 객체-지향 프로그래밍 설명 부분의 번역 입니다.


번역 : 

객체-지향 프로그램 Object-Oriented Programs

객체-지향 프로그래밍은 오늘날 프로그램의 복잡성을 극복하고 있는 듯 합니다. 프로시저를 object라고 하는 유닛으로 그룹화 함으로써 프로그램은 더 적은 블록을 요구하고 결과로 단순해 졌습니다.


OOP 새로운 것이 아니다? : Is Nothing New?

객체-지향 프로그래밍을 공부하다 보면, OOP에 대한 매우 다른 관점의 말들이 오간다는 것을 쉽게 알아 차릴 수 있습니다. OOP의 다양한 책과 문서들을 읽어보면 사람들이 전혀 다른 것에 대해 이야기 하는 것처럼 보인다. 모든 잡설들을 제고하고 나면 '혁명적 revolutionary' 과 '혁신적 evolutionary' 이라는 두가지 관점이 있다는 것을 알 수 있다. 혁명적 관점을 변론하자면 OOP는 맨손으로 프로그램을 만들던 전통적인 방법과 전혀 다르다는 것이다. 반면에 혁신가의 입장에서 보면, OOP는 전통적인 개념의 재포장 일 뿐이라고 말합니다. 아마도 각각의 견해는 맞는 것도 있고 틀린 것도 있습니다.

절차적 언어로 명확하고, 이해하기 쉬운 프로그램을 작성하는 것이 가능하다고 가정하면 혁신가의 말이 옳습니다. 그리고 객체지향 언어로 작성하더라도 이해할 수도, 관리할 수도 없는 코드가 작성될 가능성이 있습니다. 그러나 혁신가들은 OOP 프로그램이 절차적인 프로그램과는 기반적으로 다른 방법으로 구성된다는 것을 인지하지 못했습니다.


혁명가들은 OOP 디자인 프로세스가 다른 도구와 다른 추상 타입을 사용하며, OOP 프로그램은 함수적 분해를 하지 않는다는 것을 지적합니다. 혁명가들은 OOP 디자인의 명확성과 이해용이성을 과대평가 하는 듯합니다. 잘 설계되고 구현된 절차적 프로그램은 개판으로 짜여진 프로그램 보다 절대적으로 낫습니다. OOP 와 객체지향 언어는 아이디어를 명확하게 표현할 수 있는 도구를 제공하는 것이지, 즉각적으로 효과가 나타나는 만병통치약이 아닙니다. 


주: 
혁명가 : 패러다임이 완전히 다르다는 견해를 가지고 있는 사람. 
혁신가 : 이전의 기술이 개선 되었을 뿐이라는 견해를 가지고 있는 사람.



객체지향 프로그램의 다섯개의 기반 개념.

1. Objects
2. Classes
3. Encapsulation
4. Inheritance
5. Polymorphism

"객체지향 프로그램은 어떠한 목적을 이루기 위해 협업하도록 구성되어지는 객체들의 집합이다."

객체란 무엇인가? What Are Objects?

프로시저를 구조적 프로그램을 작성하는데 사용하다고 단정하면, 객체란 객체지향 프로그램을 작성하는데 사용 되는 것 입니다. 객체지향 프로그램은 어떠한 목적을 이루기 위해 협업하도록 구성되어지는 객체들의 집합입니다. 

모든 객체는 : 

데이터를 가지고 있습니다. - 데이터는 객체의 상태를 기술하는 정보를 저장합니다.
행위의 집합을 가지고 있습니다. - 이 행위들은 메세지를 받았을 때 객체가 어떻개 해야하는지 알고 있는 것 입니다.
개체를 구분하는 아이덴티티를 가지고 있습니다. - 어떠한 객체를 다른 객체와 구분하는 것을 가능케 합니다. 

구조적 프로그램에서 사용되는 레코드,구조체 처럼 객체도 데이터를 담고 있습니다. 이처럼 생각하면 객체는 이전 섹션에서 보았던 급여관리 프로그램에서 보았던 하나의 직원 레코드 처럼 보입니다. 객체의 데이터는 객체의 상태를 표현하기 위해 사용됩니다. 예를 들어, 객체의 데이터를 가지고 그 직원이 정규직원인지 파트타임인지 감지할 수 있습니다.

직원 객체는 작업 행위를 가지고 있다는 데서, 절차적 프로그램의 직원 레코드와는 다릅니다. 이 작업들은 아마도 객체의 데이터를 읽거나 변경하는데 사용될 겁니다. 객체는 자신의 등에 데이터를 메고 있는 작은 프로그램 처럼 동작합니다.


여러분이 어떤 것을 알고 싶거나, 어떤 일을 하기를 원한다면 객체에게 작업을 수행하도록 요청하면 됩니다. 이것을 객체지향 세계의 말로 "메세지" 보낸다 라고 합니다. 응답은 객체의 두번째 특성입니다.  직원 객체는 내장된 행위로 급여에 대해 어떻게 말해야 하는지, 우편물 주소 라벨을 어떻게 인쇄해야 하는지 알고 있을 것입니다.

객체의 세번째 특성은 유일한 아이덴티티를 가지고 있다는 것 입니다. 이것이 모든 객체가 관계형 데이터베이스에서 말하는 ID 값이나 Primary Key를 가져야 함을 의미하는 것은 아닙니다. 객체는 구조적 언어의 '변수'와 매우 닮았다고 할 수 있습니다. 정수 변수 i 와 j는 같은 값을 가질 수 있지만 여전히 구분 될 수 있습니다. 


클래스란 무었인가? What Are Classes?

자바 프로그램이 객체의 집합으로 이루어진다면, 대체 클래스란 뭘까? "학생" 객체의 집합인가? 아닙니다.
클래스는 객체 생성의 청사진 blueprint 입니다. 여러분은 실제 객체에 대한 코드를 절대로 작성할 수 없으며, 객체를 만드는데 사용되는 패턴을 작성할 수 있을 뿐입니다. 클래스와 객체의 구별하는 것은 미묘하지만 객체지향 디자인을 이해하기 위한 기본입니다.
 
클래스와 객체 사이의 관계를 이해하는 좋은 방법은 구조적 프로그래밍 언어의 '타입 Type' 과 '변수 Variable'를 떠올리면 됩니다. 변수의 타입을 이야기 할때 그 변수로 어떤작업을 할수 있는지, 저장할 수 있는 범위가 어떻게 되는지로 기술합니다. 예를 들어, 정수 변수는 복소수를 저장 할수 없습니다. 같은 방법으로 정수와 소수의 곱하기 연산은 가능하지만, 맞춤법 검사는 오로지 문자열에 대해서만 가능합니다.

변수는 값을 저장한다는데 주목하라 : 타입이 하는 일이 아닙니다. 타입은 추상이라고 말할 수 있습니다. 실제로 정수 값을 저장하려면 정수 변수를 생성해야만 합니다. 정수 타입으로 지정된 청사진으로 변수가 생성되면 그 변수는 정수입니다. 정수 타입에 결정된 규칙에 의해 저장되고, 행동합니다. 각각의 i,j,k 정수를 정수 타입의 '인스턴스'라고 말합니다.

객체와 클래스는 비슷한 관계를 가지고 있습니다. 클래스는 객체가 소유하게 될 속성 attributes 과 행위 behaviors 를 정의합니다. 클래스는 객체 생성의 청사진 blueprint 입니다. 자바 프로그램을 작성할 때 남이 작성한 클래스를 사용하기도 하고, 자신의 장치에 맞는 새로운 클래스를 정의하기도 합니다. 새로운 클래스를 생성하는 일은 두 부분으로 나누어져 있습니다. 

1. 객체의 상태를 저장하는데 사용될 속성을 정의 합니다.
2. 객체가 이해할 수 있는 메세지와 메세지에 응답하는 과정을 정의 합니다. 각각의 메세지에 대해 메소드 method 라고 불리우는 프로시저를 만들고, 이것을 구현합니다.


캡슐화란 무엇인가? What is Encapsulation?

객체,클래스와는 다르게 캡슐화는 자바 언어의 요소가 아닙니다. 캡슐화는 잘-설계된 클래스를 만드는데 사용되는 기법입니다. 잘-설계된 객체-지향 클래스는 캡슐화 기법을 요구합니다.

"좋아, 캡슐화라는 것에 기꺼이 주목할께, 하지만 마치 우주인이 가지고 있을 만한 물건처럼 들리는데, 도대체 그게 뭐야." 캡슐화는 프로그램을 포장 packaging 하는 작업입니다. 클래스는 두 부분으로 나눌 수 있습니다: 인터페이스 interface 와 구현 implementation.

"잠깐, 당신은 이미 클래스는 속성 attributes 과 메소드 methods 의 두-부분으로 구성되어 있다고 말했잖아요, 인터페이스와 구현은 속성과 메소드를 부르는 새로운 용어 인가요?" 라고 반응할 수 있습니다.

여러분의 객체는 속성과 메소드로 만들어져 있습니다. 일부 속성과 메소드는 객체의 외부에서 접근할 수 있고 이것을 인터페이스 interface 라고 합니다. 다른 속성,메소드는 객체 자신만의 사적인 용도로 예약되어 있고 이것을 구현 implement 이라고 합니다. 구현으로 부터 인터페이스를 분리하는 것은 객체-지향 프로그램을 설계할 때 가장 중요한 결정 입니다. 

구현으로 부터 인터페이스를 나누는 일의 가치를 살펴보자면, 자동차를 연상하면 쉬울 것입니다. 자동차의 인터페이스는  핸들, 가속 페달, 브레이크로 간단하고 규격화 되어 있습니다. 운전방법을 한번 배우기만 하면 됩니다. 반면에 자동차의 내부 동작은 점화, 실린더, 연료 분사 등등 매년 역동적으로 변경됩니다. 여러분이 각기 다른 타입의 자동차의 점화 시스템을 직접 통제해야 한다면, 새차를 운전하기란 매우 어렵다는 걸 알게 될겁니다. 

잘-설계된 클래스 Well-Designed Class 는 이런 특성을 가지고 있습니다. 인터페이스는 여러분의 클래스와 어떻게 상호작용 해야 하는지 완벽하게 묘사합니다. 그리고 클래스의 대부분의 속성은 감추어 진다는 것을 의미합니다. 사용자는 데이터를 수정하기 위해서 메소드를 사용할 것입니다.



상속이란 무엇인가? What Is Inheritance?

상속 계층을 구조화 하는 것은 클래스 설계의 두번째 결정입니다. 캡슐화는 변경으로 부터 클래스를 견고하게 만드는데 필요합니다. 상속은 클래스 관계에서 "가족 famillis" 개념과 연관이 있습니다.  

"상속의 진가는 강력한 추상 구조화 이다."

클래스의 상속관계를 정의하면 두가지의 큰 잇점을 얻을 수 있습니다. 새로운 종류의 서브클래스를 작성하면, 여려분은 부모 super class 에 이미 내장된 기능들을 사용할 수 있습니다. 이는 상속을 사용하는 가장 일반적인 잇점입니다. 그러나 이게 전부는 아닙니다. 프로그래밍 언어에서 프로시저가 처음 사용 될 때, 중복 코드를 줄이라고 요구합니다. 그리고, 복잡한 컴퓨터를 구성하고 정복하는 엄청난 힘이라는 것이 증명되었습니다.  상속의 진가는 추상화를 구조화 한다는데 있습니다. 프로시저는 복잡한 문제를 단순한 부분으로 나눌 수 있게 합니다. 상속을 사용하면 공통적인 요소를 슈퍼클래스에 정의 함으로써 일반화 할 수 있습니다. 

구조적 프로그래밍은 분해 decomposition (프로시저 형태로의 분해) 에 의한 추상화에 기반하고 있습니다. 객체-지향 프로그래밍은 추상 메커니즘을 이용해 추상화를 포함합니다. 클래스화 classification 에 의한 추상화를 기반으로 상속을 적용하는 메커니즘을 이용합니다. 자연과학에서 처럼 계층구조를 묘사할 수 있음이 계층적 클래스화의 힘입니다. 계층구조는 우리 행성의  수백만 종에 달하는 동,식물의 정보를 구조화하는 강력한 도구 입니다.


다형성이란 무엇인가? What Is Polymorphism?

마지막으로, 다형성은 객체-지향 프로그래밍의 기반 원칙입니다. "많은 형태 many shapes" 라는 뜻을 가진 그리스어에서 유래한 말로,
다형성은 캡슐화, 상속과 함께 작동해서 객체-지향 프로그램의 흐름 제어 flow of control 를 단순화 합니다.

"다형성은 캡슐화, 상속과 함께 동작 함으로써 객체-지향 프로그램의 흐름 제어를 단순화 합니다."

흐름 제어, 다음에 일어날 일을 알아햐 한다는 것은 컴퓨터 프로그램의 아킬레스 건 입니다. 이게 바로 프로그램이 복잡하게 되는 주요 원인입니다. 모든 경로를 추적하지 않고서는 객체가 가질 상태의 가능성을 검사 할 방법이 없습니다. 따라서 여러분은 프로그램을 테스트 하다가 지쳐 버릴 것입니다. 구조적 프로그래밍에서 프로그램의 흐름 제어를 단순화 하는 초기 시도는 조건 없는 분기로 규범화된 순서 구조, 선택, 반복을 제공하는 것 이었습니다. 같은 방법으로 다형성은 상속 계층의 연관된 객체에 메세지를 보냄으로써 단순화 합니다.

객체에 메세지를 보낼 때, 그 객체는 메세지에 응답할 메소드를 가지고 있어야 합니다. 클래스가 상속 계층에 연결되어 있다면, 모든 서브클래스는 부모의 인터페이스를 자동으로 상속 받습니다. 어떤 일이라도 슈퍼클래스 객체가 할 수 있는 것이라면 서브클래스 객체도 할 수 있습니다. 예를 들어, 클래스 A가 클래스 B의 서브클래스 라면, A 객체는 B객체가 할 수 있는 모든 일을 할 수 있습니다. A객체는 B객체라고 말할 수 있고, 이런 경우의 상속 관계를 ISA 관계 라고 부릅니다. 비록 서브클래스 객체가 슈퍼클래스 객체가 하는 것 처럼 동일한 메시지에 응답할 책임이 있다 하더라도, 메세지는 동일한 동작을 하도록 강제하지 않습니다. 이것을 이해할 필요가 있습니다. 각각의 서브클래스는 슈퍼클래스가 정의하고 있는 적절한 응답 또는 새로 정의된 특화된 응답에 의존하고 있습니다. 그래서, 각각의 서브클래스는 같은 메세지에 다른 응답을 할 수 있습니다. 이게 "많은 형태"라고 말하는 의미 입니다.


주 : 다음 동작에 대한 조건의 선택을 객체의 계층구조로 추상화 해 흐름 제어를 다형성으로 처리하는 구현 방법은 마틴 파울러의 리팩토링의 Replace Conditional with Polymorphism(293) 에 기술되어 있습니다.

다형성으로 조건문을 제거하라 Replace Conditional with Polymorphism(293)

객체 전문용어 가운데 가장 멋지게 들리는 말 중의 하나가 다형성(polymorphism)이다. 다형성의 진가는 동작이 그 타입에 따라 변하는 객체를 가지고 있을 때, 명시적으로 조건문을 사용하지 않아도 되도록 한다는 데 있다. 

리팩토링, 마틴 파울러 지음/윤성준,조재박 옮김, 대청, p.293 

Object-Oriented Design in Java, by Stephen Gilbert and Bill McCarty, Sams, 1998 p.32~43
 
### 끝.



저작자 표시
신고

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

   


Posted by 반더빌트


티스토리 툴바