본문 바로가기

3편 객체지향 설계 5원칙 SOLID『리스코프 치환』

by Recstasy 2020. 3. 30.

리스코프 치환의 원칙을 검색해보면 다음과 같은 설명이 나온다. 


치환성은 객체 지향 프로그래밍 원칙이다. 컴퓨터 프로그램에서 자료형 (a)가 자료형 (A)의 하위형이라면 필요한 프로그램의 속성의 변경 없이 자료형 (A)의 객체를 자료형 (a)의 객체로 교체할 수 있어야 한다는 원칙이다


역시 어렵다. 좀더 쉽게 생각해보자.


박혁거세는 알에서 태어났다.


결론적으로 박혁거세가 사람이라면 위의 문장은 '리스코프 치환'의 원칙을 어겼다. 


'박혁거세'는 사람이므로 그의 어머니는 사람이다. 사람은 영장류이며 알을 낳지 못한다. 만일 영장류라는 상위 클래스가 있다면 박혁거세의 어머니는 '영장류 클래스'를 상속받은 하위 클래스이다. 그리고 리스코프 치환의 원칙에 의하면, 하위 클래스는 특별한 변형없이 상위 클래스의 객체나 특징(기능) 그대로 사용할 수 있다. 그래서 각 개체들은 태어나자마자 특별한 교육을 받지 않더라도 부모에게서 물려받은 특성을 그대로 이용할 수 있다. 그렇다면, 뭔가 유전자 변형을 하지 않는 한 인간은 영장류의 특성대로 새끼를 낳아야 한다. 따라서 박혁거세 레전드 신화는 객체지향의 원칙, 리스코프 치환에 어긋났다.


리스코프 치환의 원칙을 지켜 객체지향 방식으로 고친다면 아래와 같아야 한다.


박혁거세는 유성생식으로 태어났다.


리스코프 치환을 왜 사용해야 할까?

객체지향 프로그래밍에서 리스코프 치환의 원칙을 사용해야하는 이유는 '복제'와 관련이 깊다. 프로그래머라면 복제를 사랑해야 한다. 왜냐하면 복제야말로 인류를 구원했으며, 인류를 한 단계씩 진보시킨 원동력이기 때문이다. 모든 것은 복제기술이 갖고 있는 '효율성'에 기원한다. 


산업혁명? 복제혁명?


농업혁명이라 불리는 1차 산업혁명은 식물을 대량으로 복제하는 기술(농업)의 발전 덕분이었다. 엄밀하게 말하자면 식물 복제라고 할 수 없지만 당시 기준으로 봤을 때 농업기술은 특정 작물을 대량으로 복제한다는 개념이었을 것이다. 이후 공업혁명이라 불리는 2차 혁명 역시 공산품을 대규모로 복제하는 기술의 발전을 의미하며, 1990년대부터 시작된 컴퓨터와 인터넷 기술의 성장은 지식과 정보를 대량으로 복제하는 혁신의 밑거름이었다. 


언제부터인지 '복제'라는 의미가 좋지 않은 느낌으로 이미지메이킹 되었는데, 복제야말로 오늘날 인류를 진보시킨 효율성의 본질이다. '복제'라는 본질은 혁명이란 단어와 연결된다. 그래서 스마트팩토리, 헬스케어, 5G, IOT, Cloud, Mobility, AI와 같은 4차 산업혁명은 3차 산업혁명의 연장선에 있는 기술이며, '4차'라는 단어를 붙일 수 없다


진정한 4차 산업혁명은 인간의 몸과 정신을 복제해서, 힘들게 자식을 낳고, 수십년 동안 인간을 교육해야하는 비효율적인 비용을 줄이는 기술이다. 즉, '나'의 육체와 정신(기억)까지 완벽하게 복제할 수 있는 기술이야말로 4차 산업혁명이라 부를 수 있다. 


자연 발생적인 유전자는 생식세포를 통해 복제를 이뤄냈지만 본능이 아닌 이성으로 살아가야만 하는 인간의 입장에서 '자식'을 생산함으로써 자신을 복제하는 행위는 비효율적이다. 동물과 달리 인간은 교육을 받아야하는 시간이 따로 있기 때문이다.  


1차 산업혁명 : 먹을 것의 복제(농업)

2차 산업혁명 : 생활용품 복제(공업)

3차 산업혁명 : 지식의 복제(컴퓨터)

4차 산업혁명 : 인간의 복제(인간  


최근 이슈되는 인공지능 기술도 결국 인간의 생각을 데이터란 형태로 복제하는 기술이다. 로봇은 인간의 몸을 복제하고 있으며, 이러한 기술 흐름은 결국 인간 스스로를 복제하는 목표의 중간 과정이다. 


리스코프 치환 사용법

복제하라

객체지향에서 '복제'를 간단하게 하는 방법은 '상속'이다. 


자바스크립트에서 상속은 extends라는 키워드를 사용하면 된다. 그리고 super()라는 메서드를 통해 상위 클래스의 특징과 기능을 그대로 복제할 수 있다.


아래 예제를 보자.


class Rectangle{

     constructor(width,height){

               this._width = width;

               this._height = height;

     }

     get width(){ return this._width; }

     get height(){ return this._height; }


     set width(value){ this._width = value }

     set height(value){ this._height = value } 


     get area(){

         return this._width * this._height;

     }

}


Rectangle클래스는 _width,_height라는 속성값을 갖고 있으며, 다음과 같은 기능을 한다.


1) 너비값 반환, 높이값 반환 || get width(), get height()

2) 너비값 입력, 높이값 입력 || set width(), set height()

3) 넓이값 계산 및 반환 || get area()


리스코프 원칙은 '복제'라는 사실을 기억하고, Rectangle클래스를 Square클래스에 복제해보자.


(...중략)


class Square extends Rectangle{

       constructor(size){

            super(size,size)

       }

       set width(value){

             this._width = this._height = value;

       }

       set height(value){

             this._width = this._height = value;

       }

}


Square클래스는 Rectangle클래스의 모든 속성과 기능을 물려받았다. 하지만 Square클래스는 이름 그대로 정사각형의 넓이값을 반환해야 하는 특수성을 지닌다. 부모에게서 물려받은 일반적인 특징을 살리면서 자신만의 특수성을 부각시키려면 어떻게 해야할까? 


방법은 간단하다. 부모에게서 물려받은 메서드명을 일치시키고, 내용을 다르게 하면 된다.(리스코프 치환) 이는 부모와 똑같은 신체적 특징(팔,다리,머리 등...)을 물려받았지만 세부적인 생김새(손금,주름,점, 등...)는 다른 것과 같은 맥락이다.



위의 물컵들은 모두 같은 상위 클래스(원본)에게서 나온 하위 클래스들이다. 물컵 겉면의 그림만 다를 뿐 속성(형태,재질)과 기능은 부모 클래스와 같다. 그리고 정사각형 클래스 역시 속성과 기능은 모두 Rectangle클래스와 같으며, _width와 _height가 같다는 세부적인 사항(물컵의 디자인)만 다를 뿐이다. 


가령, 하위 클래스를 실전에 사용한다면 아래와 같은 식이다.


let useIt = function(rc){

    let width = rc._width;

    rc.height = 10;

    console.log(`임의 넓이값은 ${10*width}, 실제 넓이값은 ${rc.area}이다`)

}


let rc = new Rectangle(2,3);

useIt(rc); // 임의 넓이값은 20, 실제 넓이값은 20이다


let sq = new Square(5);

useIt(sq);  // 임의 넓이값은 50, 실제 넓이값은 100이다


useIt함수 내의 지역변수 width의 값에는 Rectangle클래스 혹은 Rectangle클래스를 상속받은 클래스(ex.Square클래스)의 너비값(_width)이 들어간다. 그리고 rc.height를 통해 해당 클래스들의 set height()메서드를 실행해서 _height의 값을 지정한다. 위에서는 '10'을 지정했다.


Square클래스는 분명 Rectangle클래스의 속성과 기능을 모두 상속받았고, 같은 명령(set height())을 실행했음에도 불구하고 정사각형의 넓이값을 반환했다. 이는 명령어는 같더라도 세부적인 내용은 다르게 처리함으로써 디자인만 조금씩 다른 복제품들을 무수히 찍어낼 수 있다는 의미와 같다. 즉, 대량생산의 기반이자 복제의 원리와 일맥상통한다. 


결론

리스코프 치환의 원칙은 SOLID 5원칙 중에서 가장 빈번하게 사용된다. 틀을 만들어놓고 디자인만 살짝 다르게해서 대량으로 찍어내는 방식은, 비단 프로그래밍 뿐만 아니라 자본주의에서 부를 쌓는 방식으로 가장 빈번하게 사용되고 있다. 따라서 프로그래머라면 적어도 복제를 사랑하고, 어떻게든 자신의 프로그램 내에서도 복제를 맛깔나게 할지 고민할 필요가 있다.




댓글

최신글 전체

이미지
제목
글쓴이
등록일