일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- angular
- 디자인패턴
- VUE
- 코딩
- 리액트
- 프론트엔드
- It
- apollo client
- vue.js
- Funtional programming
- Node.js
- VanillaJS
- Front-End
- JavaScript
- 자바스크립트
- 함수형
- code-first
- React
- context api
- 프로그래밍
- 개발
- Programming
- goorm.io
- Ramda.js
- Design Pattern
- schema-first
- ECMAScript6
- graphql
- ELECTRON
- react.js
공부하는 블로그
#4 - 유지보수와 재사용이 쉬운 디자인을 위해 노력하라 본문
유지보수와 재사용이 쉬운 디자인을 위해 노력하라
진짜, 잘 설계 되었나?
search()
메소드를 다시 한번 살펴보자. 정말 이대로 괜찮은가? 만일 고객이 저는 12줄 기타를 찾는데요! 라고 하면 어떻게 할까? GuitarSpec에 속성을 추가하지만, Inventory 클래스도 함께 수정을 해야 할 것이다. Guitar와 Inventory 클래스는 수정할 필요가 없도록 만드는 것이 좋은 방법이다.
코드 수정
// GuitarSpec.ts
import { Builder } from "./types/Builder";
import { Type } from "./types/Type";
import { Wood } from "./types/Wood";
export type GuitarSpec = InstanceType<typeof GuitarSpec>;
export const GuitarSpec = class {
private _builder: Builder;
private _model: string;
private _type: Type;
private _backWood: Wood;
private _topWood: Wood;
private _numStrings: number;
constructor(
builder: Builder,
model: string,
type: Type,
numStrings: number,
backWood: Wood,
topWood: Wood
) {
this._builder = builder;
this._model = model;
this._type = type;
this._backWood = backWood;
this._topWood = topWood;
this._numStrings = numStrings;
}
get builder(): Builder {
return this._builder;
}
get model(): string {
return this._model;
}
get type(): Type {
return this._type;
}
get backWood(): Wood {
return this._backWood;
}
get topWood(): Wood {
return this._topWood;
}
get numStrings(): number {
return this._numStrings;
}
public matches(otherSpec: GuitarSpec): boolean {
const { builder, model, type, backWood, topWood, numStrings } = this;
const {
builder: otherBuilder,
model: otherModel,
type: otherType,
backWood: otherBackWood,
topWood: otherTopWood,
numStrings: otherNumStrings
} = otherSpec;
if (builder !== otherBuilder) return false;
if (model && model !== otherModel) return false;
if (type !== otherType) return false;
if (backWood !== otherBackWood) return false;
if (topWood !== otherTopWood) return false;
if (numStrings !== otherNumStrings) return false;
return true;
}
};
//Inventory.ts
import { Guitar } from "./Guitar";
import { GuitarSpec } from "./GuitarSpec";
export type Inventory = InstanceType<typeof Inventory>;
export const Inventory = class {
private _guitars: Guitar[];
constructor() {
this._guitars = new Array<Guitar>();
// this._guitars = [];
}
addGuitar(serialNumber: string, price: number, spec: GuitarSpec): void {
const guitar: Guitar = new Guitar(serialNumber, price, spec);
this._guitars.push(guitar);
}
getGuitar(serialNumber: string): Guitar {
return this._guitars.filter(
(item: Guitar): boolean => item.serialNumber == serialNumber
)[0];
}
search(searchSpec: GuitarSpec): Guitar[] {
return this._guitars.filter(
(item: Guitar): boolean => {
return searchSpec.matches(item.spec);
}
);
}
};
import { Inventory } from "./Inventory";
import { Guitar } from "./Guitar";
import { Builder } from "./types/Builder";
import { Type } from "./types/Type";
import { Wood } from "./types/Wood";
import { GuitarSpec } from "./GuitarSpec";
const FindGuitarTester = class {
public main(): void {
const inventory: Inventory = new Inventory();
this.initInventory(inventory);
const whatErinLikes: GuitarSpec = new GuitarSpec(
Builder.FENDER,
"Stratocastor",
Type.ELECTRIC,
6,
Wood.ALDER,
Wood.ALDER
);
const guitars: Guitar[] = inventory.search(whatErinLikes);
if (guitars.length > 0) {
guitars.forEach((guitar: Guitar) => {
const { builder, model, type, backWood, topWood } = guitar.spec;
console.log(`Erin, you might like this ${builder} ${model} ${type} guitar:
${backWood} back and sides,
${topWood} top.
You can have it for only ${guitar.price}!`);
});
} else {
console.log("Sorry, Erin, we have noting for you.");
}
}
private initInventory(inventory: Inventory) {
inventory.addGuitar(
"11277",
3999.95,
new GuitarSpec(
Builder.COLLINGS,
"CJ",
Type.ACOUSTIC,
6,
Wood.INDIAN_ROSEWOOD,
Wood.SITKA
)
);
inventory.addGuitar(
"V95693",
1499.95,
new GuitarSpec(
Builder.FENDER,
"Stratocastor",
Type.ELECTRIC,
6,
Wood.ALDER,
Wood.ALDER
)
);
inventory.addGuitar(
"V9512",
1549.95,
new GuitarSpec(
Builder.FENDER,
"Stratocastor",
Type.ELECTRIC,
6,
Wood.ALDER,
Wood.ALDER
)
);
inventory.addGuitar(
"122784",
5495.95,
new GuitarSpec(
Builder.MARTIN,
"D-18",
Type.ACOUSTIC,
6,
Wood.MAHOGANY,
Wood.ADIRONDACK
)
);
inventory.addGuitar(
"76531",
6295.95,
new GuitarSpec(
Builder.MARTIN,
"OM-28",
Type.ACOUSTIC,
6,
Wood.BRAZILIAN_ROSEWOOD,
Wood.ADIRONDACK
)
);
inventory.addGuitar(
"70108276",
2295.95,
new GuitarSpec(
Builder.GIBSON,
"Les Paul",
Type.ELECTRIC,
6,
Wood.MAHOGANY,
Wood.MAHOGANY
)
);
inventory.addGuitar(
"82765501",
1890.95,
new GuitarSpec(
Builder.GIBSON,
"SG '61 Reissue",
Type.ELECTRIC,
6,
Wood.MAHOGANY,
Wood.MAHOGANY
)
);
inventory.addGuitar(
"77023",
6275.95,
new GuitarSpec(
Builder.MARTIN,
"D-28",
Type.ACOUSTIC,
6,
Wood.BRAZILIAN_ROSEWOOD,
Wood.ADIRONDACK
)
);
inventory.addGuitar(
"1092",
12995.95,
new GuitarSpec(
Builder.OLSON,
"SJ",
Type.ACOUSTIC,
12,
Wood.INDIAN_ROSEWOOD,
Wood.CEDAR
)
);
inventory.addGuitar(
"566-62",
8999.95,
new GuitarSpec(
Builder.RYAN,
"Cathedral",
Type.ACOUSTIC,
12,
Wood.COCOBOLO,
Wood.CEDAR
)
);
inventory.addGuitar(
"6 29584",
2100.95,
new GuitarSpec(
Builder.PRS,
"Dave Navarro Signature",
Type.ELECTRIC,
6,
Wood.MAHOGANY,
Wood.MAPLE
)
);
}
};
new FindGuitarTester().main();
코드를 보면 search()
메소드에서 직접 비교하던 속성들을 GuitarSpec.matches()
에 위임하였다. 위임이란, 객체가 어떤 일을 할 때 직접하지 않고 다른 객체가 그 일을 하는 것을 말한다. 위임을 통해 우리는 코드의 재사용성을 높일 수 있으며 한 객체의 기능을 여러곳에 분산할 필요가 없다.
위임
위임은 각 객체가 동일성을 스스로 해결하게 한다. 객체가 서로 독립적이고 더 느슨하게 결합되게 한다. 그래서 다른 프로그램에서도 빼내어 재사용하기 쉽다. (느슨하게 결합되었다는 말은 각 객체가 자신의 일만을 수행하는 것을 말한다. 이는 유연하게 코드 수정이 가능하다.)
위임: 한 객체가 기능(operation)을 다른 객체에 넘겨주어 첫번째 객체를 대신해서 수행하도록 하는 행위
OOAD란 (Object Oriented Analysis & Design)
고객은 프로그램이 동작할 때 만족스러워한다.
고객이 원하는 것을 만들어주기 위해 우리는 고객으로부터 요구사항을 얻는다. 유스케이스들과 다이어그램이 도움이 되지만 이는 고객이 원하는 프로그램을 알아내기 위한 것일 뿐이다.
개인 적용: 오늘 같은 경우에는 카카오 Oauth 로그인 기능 구현하는데 구조적으로 짜려는 욕심을 내다가, 클라이언트들이 오랜 시간 기다리는 일이 있었다. 사실 기능이 잘 동작하는지도 모르는 상태에서 구조적인 프로그래밍을 하려다가 동료 개발자분께서 일단 기능 먼저 테스트해보자고 해서 배포 후 기능동작을 확인 후에 리팩토링을 하게 되었다.
고객은 프로그램이 계속 잘 동작할 때 만족스러워한다.
어제까지 잘 되다가 오늘 잘 안되면 좋아할 사람은 없을 것이다. 프로그램을 잘 설계한다면 고객이 이상한 방식으로 조작해도 깨지지 않을 것. 클래스 다이어그램과 시퀀스 다이어그램이 설계의 문제점을 나타내는데 도움을 주지만, 핵심은 잘 설계된 견고한 프로그램을 작성하는 것이다.
고객은 프로그램이 업그레이드가 가능할 때 만족스러워한다.
고객이 간단한 새 기능을 추가하기 원하는데, 2주 걸리고 2500만원이 든다? 좋아하지 않을 것이다. 캡슐화, 구성, 위임의 객체지향 기법을 사용하면 유지보수가 쉽고 확장성이 좋은 프로그램을 만들 수 있다.
개인 적용: 알림톡 메시지 타입을 추가했는데, 기능이 더이상 동작을 안한다면 우리 대표님은 나에게 많은 실망을 하겠지. 알림톡 타입을 추가하면서 나 자신도 불안해하고, 어떻게 기능을 확장해야할지 전전긍긍하며 스트레스 받는다면 해당 기능은 분명 문제가 있는 것이리라. 😭
프로그래머는 자신의 프로그램이 재사용될 수 있을 때 만족스러워 한다.
프로그램을 만들고, 거의 같은 프로그램을 다른 고객에게 제공할 때 사용 불가능 한 경우를 경험해 봤는가? 조금만 분석하면 복잡한 의존 관계와 연관 관계를 피해 쉽게 재사용 할 수 있게 만들 수 있다. (OCP, SRP 등…)
프로그래머는 자신의 프로그램이 유연할 때 만족스러워 한다.
가끔은 약간의 리팩토링을 통해서 좋은 프로그램을 다양한 종류의 일에 사용할 수 있는 훌륭한 프레임웍으로 바꿀 수 있다. (아키텍트로 가는 첫걸음)
핵심 정리
깨지기 쉬운 프로그램은 조금만 잘못 조작해도 문제가 발생한다.
캡슐화와 위임 같은 객체지향 원리를 사용하여 유연한 프로그램을 만들 수 있다.
캡슐화는 프로그램을 여러 개의 논리적 부분들로 나눈다.
위임은 특정한 일을 해결하는 책임을 다른 객체에게 주는 것이다.
프로젝트는 항상 고객이 원하는 것을 알아내는 것부터 시작한다.
프로그램의 기본 기능을 구현한 후에 설계를 우연하게 가다듬는 데 노력하라.
기능과 유연한 설계가 완성이 되면, 디자인 패턴을 사용해서 프로그램의 디자인을 개선하고, 재사용이 용이하게 만든다.
프로그램 중 자주 변경을 요하는 부분을 찾아서 변경되지 않는 부분과 분리해 놓으라.
잘 동작하지만 설계가 엉망인 프로그램의 경우에 고객은 만족시키지만 문제를 고치느라 수고, 고통, 밤샘 등을 겪을 가능성이 크다.
'design patterns' 카테고리의 다른 글
#6 - 요구사항 변경 (0) | 2019.03.26 |
---|---|
#5 - 요구사항 분석 (0) | 2019.03.23 |
#3- 객체지향의 기본 원리를 이용해서 소프트웨어를 유연하게 하라 (0) | 2019.03.18 |
#2 - 고객이 원하는 기능을 하게 하라 (0) | 2019.03.16 |
#1 - 잘 설계된 프로그램이 세상을 뒤흔든다 (0) | 2019.03.13 |