ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 디자인 원칙 - 구현이 아닌 인터페이스에 대해 프로그래밍
    Web 개발/디자인 패턴 2022. 12. 21. 19:16

    구현이 아닌 인터페이스에 대해 프로그래밍

    구현이 아닌 인터페이스에 대해 프로그래밍하라, 또 구상 클래스에 의존하는 대신 추상화에 의존하라

    기존의 코드를 망가뜨리지 않고 쉽게 확장할 수 있다면 그 디자인은 충분히 유연하다고 말할 수 있다.

     

    만약 두 클래스가 서로 같이 작업하도록 만들려면 둘 중 하나를 다른 클래스에 의존하게 만드는 것으로 시작할 수 있다. 그런데 객체간의 공동 작업을 만드는 더 유연한 방법이있다.

     

    1. 한 객체가 다른 객체에서 정확히 무엇을 필요로하는지 확인하세요 어떤 메서드들을 실행하나요?
    2. 새 인터페이스 또는 추상 클래스에서 이러한 메서드들을 설명하세요.
    3. 다른 객체에 의존하는 (dependency인) 클래스가 아닌 인터페이스를 구현하도록 하세요.
    4. 이제 두 클래스를 구상 클래스가 아닌 이 인터페이스에 의존하도록 하도록 하세요 여전히 원래 클래스의 객체들과 작동하도록 만들 수 있지만 이제 연결이 훨씬더 유연합니다.

    인터페이스를 추출하기 전과 후 

     

    예시

    아래 예시는 구상클래스에 의존하는 것보다 인터페이스를 통해 작업하는 것이 더 유익할 수 있음을 보여준다.

     

    수정전 : 모든 클래스가 단단히 결합되어 있다.

    import { Designer } from "./designer";
    import { Programmer } from "./programmer";
    import { Tester } from "./tester";
    
    export class Company {
      createSoftware() {
        const d = new Designer();
        const p = new Programmer();
        const t = new Tester();
    
        d.designArchitecture();
        p.writeCode();
        t.testSoftWare();
      }
    }

     

    Company(회사) 클래그사 직원들의 구상 클래들과 밀접하게 결합이되어 있다. Designer, Programmer, Tester 3개의 클래스의 구현의 차이에도 불구하고, 다양한 업무 관려 메서드들을 일반화한 다음 모든 직원클래스에 대한 공통 인터페이스를 추출할 수 있다.

     

    수정후 :  다형성은 코드를 단순화하는 데 도움이 되었지만 Company클래스의 나머지 부분은 여전히 구상 직원 클래스들에 의존한다.

    import { Designer } from "./designer";
    import { Programmer } from "./programmer";
    import { Tester } from "./tester";
    
    export class Company {
      createSoftware() {
        const employee = [new Designer(), new Programmer(), new Tester()];
    
        employee.forEach((e) => {
          e.doWork();
        });
      }
    }

     

    Company클래스는 여전히 직원 클래스에 연결되어 있다. 이건 바람직한 모습이라고 할 수 없다. 왜냐하면 다른 유형(직무)의 직원과 함께 일하게 되는 경우 새로운 유형을 회사에 도입하려면 Company클래스의 코드를 재사용하는대신 Employee 구현해 클래스의 대부분을 오버라이드해야 하기 때문이다.

     

    이 문제를 해결하기 위해 직원들을 가져오는 메서드를 abstract로 선언할 수 있다. 각 구상 회사는 이 메서드를 다르게 구현할 것이며, 자기회사에 필요한 직원들만 만들것이다.

     

    *추상화 : 추상 클래스를 정의할 때는 class 앞에 abstract라고 표기한다. 또한 추상메서드를 정의할 때도 abstract를 메서드 이름 앞에 붙인다. 추상 메소드는 정의만 있을 뿐 몸체(body)가 구현되어 있지 않다.  추상 클래스를 상속하는 클래스에서 해당 추상 메소드를 통해 몸체를 필히 구현해야한다.

    그리고 추상메서드는 실사용가능한 메서드 또한 정의할 수 있다.추상 클래스를 상속하는 클래스를 통해 생성된 인스턴스를 통해 추상메서드에 정의된 메서드를 사용할 수 있다. 추상클래스는 말 그대로 추상 클래스이므로 실제로 인스턴스를 생성 할 수 없다.

    객체 관계

     

     

    수정후: Company 클래스의 기본 메서드는 구상 직원 클래스들에 독립적이다. 직원 객체들은 구상 회사 자식 클래스들에서 생성된다.

     

    Company

    import { Designer } from "./designer";
    import { Programmer } from "./programmer";
    import { Tester } from "./tester";
    
    export abstract class Company {
      public abstract getEmployees();
    
      public createSoftware() {
        const employee = this.getEmployees();
    
        employee.forEach((e) => {
          e.doWork();
        });
      }
    }

    GameDev Company

    import { Company } from "./company";
    import { Designer } from "./designer";
    import { Artist } from "./artist";
    
    export class GameDevCompany extends Company {
      public getEmployees() {
        return [new Designer(), new Artist()];
      }
    
     //구현하지 않아도 이미 createSoftware()메서드가 정의된 것과 같다.
    }

    Outsourcing Company

    import { Company } from "./company";
    import { Programmer } from "./programmer";
    import { Tester } from "./tester";
    
    export class OutsourcingCompany extends Company {
      public getEmployees() {
        return [new Programmer(), new Tester()];
      }
    
      //구현하지 않아도 이미 createSoftware()메서드가 정의된 것과 같다.
    }

    이렇게 변경한 후 Company클래스는 다양한 직원 클래스는 다양한 직원 클래스에서 독립되었다. 이제 기초 회사 클래스의 일부를 계속 재사용하면서 해당 클래스를 확장하여 새로운 유형들의 회사들과 직원들을 소개할 수 있다. 기초 회사 클래스를 확장해도 이미 그에 의존하고 있는 기존코드는 손상되지 않는다.

     

    앞서 나온 내용이 바로 팩토리 메서드 패턴의 예시였다.

    댓글

Designed by Tistory.