-
디자인 원칙 - 구현이 아닌 인터페이스에 대해 프로그래밍Web 개발/디자인 패턴 2022. 12. 21. 19:16
구현이 아닌 인터페이스에 대해 프로그래밍
구현이 아닌 인터페이스에 대해 프로그래밍하라, 또 구상 클래스에 의존하는 대신 추상화에 의존하라
기존의 코드를 망가뜨리지 않고 쉽게 확장할 수 있다면 그 디자인은 충분히 유연하다고 말할 수 있다.
만약 두 클래스가 서로 같이 작업하도록 만들려면 둘 중 하나를 다른 클래스에 의존하게 만드는 것으로 시작할 수 있다. 그런데 객체간의 공동 작업을 만드는 더 유연한 방법이있다.
- 한 객체가 다른 객체에서 정확히 무엇을 필요로하는지 확인하세요 어떤 메서드들을 실행하나요?
- 새 인터페이스 또는 추상 클래스에서 이러한 메서드들을 설명하세요.
- 다른 객체에 의존하는 (dependency인) 클래스가 아닌 인터페이스를 구현하도록 하세요.
- 이제 두 클래스를 구상 클래스가 아닌 이 인터페이스에 의존하도록 하도록 하세요 여전히 원래 클래스의 객체들과 작동하도록 만들 수 있지만 이제 연결이 훨씬더 유연합니다.
인터페이스를 추출하기 전과 후
예시
아래 예시는 구상클래스에 의존하는 것보다 인터페이스를 통해 작업하는 것이 더 유익할 수 있음을 보여준다.
수정전 : 모든 클래스가 단단히 결합되어 있다.
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클래스는 다양한 직원 클래스는 다양한 직원 클래스에서 독립되었다. 이제 기초 회사 클래스의 일부를 계속 재사용하면서 해당 클래스를 확장하여 새로운 유형들의 회사들과 직원들을 소개할 수 있다. 기초 회사 클래스를 확장해도 이미 그에 의존하고 있는 기존코드는 손상되지 않는다.
앞서 나온 내용이 바로 팩토리 메서드 패턴의 예시였다.
'Web 개발 > 디자인 패턴' 카테고리의 다른 글
싱클톤(Singleton)패턴 구현2 (0) 2023.06.17 싱글톤(Singleton)패턴1 (0) 2023.06.17 디자인 원칙 - 변화하는 내용을 캡슐화 (0) 2022.12.20 디자인패턴 - 소개 (0) 2022.12.19 객체 간의 관계(의존성,연관관계....) (0) 2022.12.19