์ฐธ๊ณ ๊ธ
Composition VS Extends : delegation, decorator, wrapper
Effective Java : ์์ดํ 18. (๊ธฐ๋ฅ ํ์ฅ์ด ํ์ํ ๋)์์๋ณด๋ค๋ ์ปดํฌ์ง์ ์ ์ฌ์ฉํ๋ผ [Effective Java] 4์ฅ ํด๋์ค์ ์ธํฐํ์ด์ค ์์์ด๋? extends๋ฅผ ๋งํจ. (implements๋ ์๋. ์ด๊ฑด ๊ตฌํ.) ์ปดํฌ์ง์ ์ด๋? Compo
umbum.dev
๋งํฌ ์์ฝ:
์์์ extends
์ปดํฌ์ง์ ์ ํ์ํ ๊ฐ์ฒด๋ฅผ ๋ด๋ถ private ๋ณ์๋ก ๋๋ ๊ฒ(ํด๋์ค๊ฐ ๋ค๋ฅธ ํด๋์ค์ ๊ตฌ์ฑ์์๋ก ์ฐ์ธ๋ค๋ ๋ป)
์์๊ณผ ์ปดํฌ์ง์ ์ ์ฐจ์ด๋ ์ค๋ฒ๋ผ์ด๋ฉํ๋๋ ๋ฉ์๋๋ฅผ ํธ์ถํด์ delegateํ๋๋์ ์ฐจ์ด๋ก ๋ณผ ์ ์๋ค.
0. ์์ vs ๋ธ๋ฆฌ๊ฒ์ดํธ ์์(์ปดํฌ์ง์ )
์์์, ์์ง์ ์ธ ๊ด๊ณ๋ฅผ ์ด์ฉํด์ ์ฝ๋๋ฅผ ํ์ฅํ๊ณ ์ฌ์ฌ์ฉํ๋ค๋ ๊ฐ๋ ์ด๋ค. ๋จ ํ๋์ ํด๋์ค๋ง ์์๋ฐ์ ์ ์๋ค(๋ค์ค์์ ๋ถ๊ฐ), ์์์ด ๋ ์ ์ ํ๋ฐ๋ ์์์ ๋จ์ฉํ๋ฉด ์ด์น์ ์ ๋ง๋ ์ผ์ด์ค๊ฐ ๋ฐ์ํ ์ ์๊ณ , ์ ์ง๋ณด์๊ฐ ์ด๋ ต๋ค
๊ทธ๋ฌ๋ฏ๋ก, ์์์ ํด์ผํ (ํ์ํ) ๊ฒฝ์ฐ๋ฅผ ๋จ๊ฒจ๋๋ ๊ฒ์ด ์ข๋ค.
์์์, ๋ ๊ณ ๋ฅผ ์กฐ๋ฆฝํ๋ ๊ฒ์ฒ๋ผ ํ์ํ ๋ถํ์ ์ธ๋ถ๋ก๋ถํฐ ์ฃผ์ ๋ฐ์์ ์ฃผ์ ๋ฐ์ ๊ทธ ๋ถํ์ ํ์ฉํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ํธํ๋ค.
1. ํด๋์ค ์์์ ์ ์ฉํจ
//์์
class Printer {
print() {
console.log('๊ธฐ๋ณธ์ ์ธ ์ถ๋ ฅ');
}
}
class ColorPrinter extends Printer {
print() {
console.log('์ปฌ๋ฌ ์ถ๋ ฅ!');
}
}
class BlackPrinter extends Printer {
print() {
console.log('ํ๋ฐฑ ์ถ๋ ฅ!');
}
}
const printers = [new Printer(), new ColorPrinter(), new BlackPrinter()];
printers.forEach((printer) => printer.print());
์ถ๋ ฅ : ๋คํ์ฑ์ ๋ํ์ ์ธ ์์๋ผ๊ณ ํ ์ ์๋ค.
2. ์์ ๋์ composition(์ปดํฌ์ง์ :์์)์ ์ด๋ค๋ฉด
//์ปดํฌ์ง์
(์์)
class Printer {
#delegate; //๋ฌผ๋ก ์ด๋ฐ ์ด๋ฆ์ ๋์ ๋์๊ฐ ๋๋ฏ๋ก, printerHeader ๋ฑ์ผ๋ก ๋ฐ๊พธ๋ ๊ฒ์ด ์ข๋ค.
constructor(delegate) {
this.#delegate = delegate;
}
print() {
//์ ๋ฌ๋ฐ์ ๋ธ๋ฆฌ๊ฒ์ดํธ๊ฐ ์๋ค๋ฉด ์คํ, ์๋ค๋ฉด ๊ธฐ๋ณธ ์ถ๋ ฅ
this.#delegate ? this.#delegate.print() : console.log('๊ธฐ๋ณธ์ ์ธ ์ถ๋ ฅ');
}
}
class ColorPrinterHeader {
print() {
console.log('์ปฌ๋ฌ ์ถ๋ ฅ!');
}
}
class BlackPrinterHeader {
print() {
console.log('ํ๋ฐฑ ์ถ๋ ฅ!');
}
}
//ํ๋ฆฐํฐ๋ฅผ ๋ง๋ค ๋, ๋ธ๋ฆฌ๊ฒ์ดํธ ์ฉ ํด๋์ค(์์ํ ์ ์๋ ์ธ์คํด์ค)๋ฅผ ์ฃผ์
ํด์ค๋ค
const printers = [new Printer(), new Printer(new ColorPrinterHeader()), new Printer(new BlackPrinterHeader())];
printers.forEach((printer) => printer.print());
์ถ๋ ฅ : ์ด๋ ๋ฏ ์์์ ์ด์ฉํ๋ฉด ๋์ผํ ํด๋์ค๋ฅผ ์ด์ฉํด์ ์ ๋ฌ๋ ์์์์ ๋ฐ๋ผ์ ๋ค๋ฅธ ํ๋์ ํ๋๋ก ๋ง๋ค ์ ์๋ค.
์ปดํฌ์ง์ ์ ์ด์ฉํ๋ฉด ๋ ๊ณ ๋ฅผ ์กฐ๋ฆฝํ๋ฏ์ด ํ์ํ ๊ธฐ๋ฅ๋ค์ ํ๋ํ๋์ฉ ๋ฌถ์ด์ ์กฐ๋ฆฝํด๋๊ฐ ์ ์๋ค.
3. ์ปดํฌ์ง์ ์ฝ๋๋ฅผ ts๋ก ์ ํํ๋ค๋ฉด
//์์์ ์ฌ์ค ํ์
์คํฌ๋ฆฝํธ๋ก ํ ๋ ๋์ฑ ํ๋ถํ ์๋์ง๋ฅผ ๊ฐ์ง ์ ์๋ค.
class Printer {
//์ด๋ ๋ฏ ๋ธ๋ฆฌ๊ฒ์ดํธ ์ฃผ์
ํ ๋๋ถํฐ private ๋ฌ์์ฃผ๋ฉด #์ ์น๋ค ์ ๊ฑฐํด๋ ๋๋ค! ํจ์ฌ ๊น๋ํด์ง๋ค.
//PrinterHeader์ ์ต์
๋๋ก ์ง์ ํ์ฌ ์ปดํฌ์ง์
(์์: ๋ธ๋ฆฌ๊ฒ์ดํธ)๊ฐ ์์ด๋ ์์ด๋ ๋จ์ ๋ช
์ํด์ค๋ค.
constructor(private delegate?: PrinterHeader) { //๋ํ ์๋ฌด๊ฑฐ๋ ์ ๋ฌํ ์ ์๊ฒ ํ์
์ ์ง์ ํ ์ ์๋ค.
this.delegate = delegate;
}
print() {
this.delegate ? this.delegate.print() : console.log('๊ธฐ๋ณธ์ ์ธ ์ถ๋ ฅ');
}
}
//PrinterHeader๊ฐ ๋๋ ค๋ฉด ์ด ๊ท๊ฒฉ(interface: ์ธํฐํ์ด์ค)์ ๋ฐ๋ผ์ผ ํด! ๊ท์น์ ์ ํด์ค๋ค.
//ํด๋์ค๋ ๊ฐ์ฒด์ ์ฒญ์ฌ์ง์ด๋ผ๋ฉด ์ธํฐํ์ด์ค๋ ํด๋์ค์ ์ฒญ์ฌ์ง์ด๋ค.
//ํด๋์ค๊ฐ ํด์ผํ๋ ํ๋์ ๊ฒฐ์ : ์ฆ, ํด๋์ค๊ฐ ์ด๋ค ๋ฉ์๋๋ฅผ ๊ฐ์ง์ง ๊ฒฐ์ ํ๋ค.
//ํด๋์ค๋ ์ธํฐํ์ด์ค๋ฅผ implement(๊ตฌํ)ํ๋ค.
interface PrinterHeader {
//ํ๋ฆฐํธ๋ผ๋ ํจ์๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ฉฐ, ์๋ฌด ๊ฒ๋ ๋ฐํํ์ง ์์์ผ ํ๋ค.
print(): void;
}
class ColorPrinterHeader implements PrinterHeader {
print() {
console.log('์ปฌ๋ฌ ์ถ๋ ฅ!');
}
}
class BlackPrinterHeader implements PrinterHeader {
print() {
console.log('ํ๋ฐฑ ์ถ๋ ฅ!');
}
}
const printers = [new Printer(), new Printer(new ColorPrinterHeader()), new Printer(new BlackPrinterHeader())];
printers.forEach((printer) => printer.print());
๊ทธ๋ฐ๋ฐ Printer์ delegate๋ฅผ null์ด ๋ ์ ์๊ฒ private๋ก ๋๊ณ ์๋์ ๊ฐ์ด ๋ฐ๊ฟ ์๋ ์๋ค.
class Printer {
private delegate: PrinterHeader;
constructor(delegate?: PrinterHeader) {
this.delegate = delegate ? delegate : new DefaultPrinterHeader();
}
print() {
this.delegate.print();
}
}
interface PrinterHeader {
print(): void;
}
class DefaultPrinterHeader implements PrinterHeader {
print() {
console.log('๊ธฐ๋ณธ ์ถ๋ ฅ!');
}
}
class ColorPrinterHeader implements PrinterHeader {
print() {
console.log('์ปฌ๋ฌ ์ถ๋ ฅ!');
}
}
class BlackPrinterHeader implements PrinterHeader {
print() {
console.log('ํ๋ฐฑ ์ถ๋ ฅ!');
}
}
const printers = [new Printer(), new Printer(new ColorPrinterHeader()), new Printer(new BlackPrinterHeader())];
printers.forEach((printer) => printer.print());