Pro typescript.ch03.Object Orientation in TypeScript

Preview:

Citation preview

Pro TypeScriptCh.03 Object Orientation in Type-Script

윤석준 (seokjoon.yun@gmail.-com)

2

Agenda Object Orientation in TypeScript

SOLID Principles

Design Patterns

Mixins

Object Orientation in TypeScript- Classes- Instance of

classes- Methods- Inheritance- Open Recur-

sion- Encapsulation- Delegation- Polymophism

https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)

https://en.wikipedia.org/wiki/This_(computer_programming)#Open_recursionhttps://en.wikipedia.org/wiki/Encapsulation_(computer_programming)

https://en.wikipedia.org/wiki/Delegation_(computing)

https://en.wikipedia.org/wiki/Polymorphism_(computer_science)

Ch.01

5

Open Recursion

Combination of recursion and late binding 멤버 함수가 자기 자신을 호출시 서브클래스에 정의된 함수로 대체될 수 있음

이거 그냥 override 아니에요 ? No, no, no. Recursion and late binding.

그래도 그냥 함수 override 인데…

6

ex. Open Recursioninterface FileItem { path: string; contents: string[];}class FileReader { getFiles(path: string, depth: number = 0) { var fileTree = []; var files = fs.readdirSync(path);

for (var i = 0; i < files.length; i++) { var file = files[i]; var stats = fs.statSync(file); var fileItem; if (stats.isDirectory()) { // Add directory and contents fileItem = { path: file, contents: this.getFiles(file, (depth + 1)) }; } else { // Add file fileItem = { path: file, contents: [] }; } fileTree.push(fileItem); } return fileTree; }}

class LimitedFileReader extends FileReader { constructor(public maxDepth: number) { super(); } getFiles(path: string, depth = 0) { if (depth > this.maxDepth) { return []; } return super.getFiles(path, depth); }}

// instatiating an instance of LimitedFileReadervar fileReader = new LimitedFileReader(1);

// results in only the top level, and one additional level being readvar files = fileReader.getFiles('path');

7

Encapsulation

Private 제한자를 사용해 변수 , 함수를 외부로부터 숨김 외부에 알리고 싶지 않은 것을 숨길 수 있음

외부에서 몰라도 되는 것을 숨기는 것은 추상화 (Abstraction)

추상화나 캡슐화나… 내가 보기엔 다 거서 건데…

8

ex. Encapsulationclass Totalizer { private total = 0; private taxRateFactor = 0.2;  addDonation(amount: number) { if (amount <= 0) { throw new Error('Donation exception'); } var taxRebate = amount * this.taxRateFactor; var totalDonation = amount + taxRebate; this.total += totalDonation; }  getAmountRaised() { return this.total; }} var totalizer = new Totalizer();totalizer.addDonation(100.00); var fundsRaised = totalizer.getAmountRaised(); // 120console.log(fundsRaised);

9

Delegation

Wrapper class 로 원래 class 를 감싸는 형태 상속관계 (is a) 가 아닌 경우의 좋은 대안

코드 재사용성 측면에 있어서 가장 유용한 개념

10

Delegation vs Inheritance Delegation : Has

A

Ingeritance : Is

A

11

ex. Delegationinterface ControlPanel { startAlarm(message: string): any;} interface Sensor { check(): any;} class MasterControlPanel { private sensors: Sensor[] = []; constructor() { // Instantiating the delegate HeatSensor this.sensors.push(new HeatSensor(this)); }  start() { for (var i= 0; i < this.sensors.length; i++) { // Calling the delegate this.sensors[i].check(); } window.setTimeout(() => this.start(), 1000); }  startAlarm(message: string) { console.log('Alarm! ' + message); }}

class HeatSensor { private upperLimit = 38; private sensor = { read: function() { return Math.floor(Math.random() * 100); } };  constructor(private controlPanel: ControlPanel) { }  check() { if (this.sensor.read() > this.upperLimit) { // Calling back to the wrapper this.controlPanel.startAlarm('Overheating!'); } }} var cp = new MasterControlPanel();

cp.start();

12

Polymorphism

같은 함수 시그너쳐를 여러가지로 다르게 구현 An interface implemented by many classes An interface implemented by many objects An interface implemented by many functions A superclass with a number of specialized sub-classes

Any structure with many similar structures

13

ex. Polymorphisminterface Vehicle { moveTo(x: number, y: number);} class Car implements Vehicle { moveTo(x: number, y: number) { console.log('Driving to ' + x + ' ' + y); }} class SportsCar extends Car {} class Airplane { moveTo(x: number, y: number) { console.log('Flying to ' + x + ' ' + y); }} function navigate(vehicle: Vehicle) { vehicle.moveTo(59.9436499, 10.7167959);} var airplane = new Airplane();navigate(airplane);var car = new SportsCar();navigate(car);

SOLID Principles

15

SOLID Principle S : Single responsibility principle

O : Open/Closed principle

L : Liskov substitution principle

I : Interface segregation principle

D : Dependency inversion principle

16

Single responsibility princi-ple

관련성이 있는 코드들을 클래스 , 모듈에 모아야 함 . 함우의 경우에도 한 가지 일만 수행해도록 나눠야 함 . (Uncle Bob : http://blog.cleancoder.com

)

작업하다보면 SRP 기준에 부합하는지 어긋나는지 나누기가 애매한 경우가 많음

단일 책임의 원칙

클래스는 오직 한가지 작업만 수행해야 하며 ,한 가지 이유에 의해서만 변경되어야 함 .

17

ex. Single responsibility principle (SRP) vio-lationclass Movie { private db: DataBase; constructor(private title: string, private year: number) { this.db = DataBase.connect('user:pw@mydb', ['movies']); } getTitle() { return this.title + ' (' + this.year + ')'; } save() { this.db.movies.save({ title: this.title, year: this.year }); }}

18

ex. Separate reasons for changeclass Movie { constructor(private title: string, private year: number) { } getTitle() { return this.title + ' (' + this.year + ')'; }} class MovieRepository { private db: DataBase; constructor() { this.db = DataBase.connect('user:pw@mydb', ['movies']); } save(movie: Movie) { this.db.movies.save(JSON.stringify(movie)); }} // Movievar movie = new Movie('The Internship', 2013);// MovieRepositoryvar movieRepository = new MovieRepository();

movieRepository.save(movie);

19

Open/closed principle

변경가능성을 염두해 두고 적응형 코드로 작성을 하되 , 변경 사항에 대해서는 기존 코드를 수정할 필요 없게 만들어야 한다 .

개방 / 폐쇄 원칙확장에 대해서는 개방적이어여 하고 ,수정에 대해서는 폐쇄적이어야 한다 .

20

ex. Open–closed principle (OCP)class RewardPointsCalculator { getPoints(transactionValue: number) { // 4 points per whole dollar spent return Math.floor(transactionValue) * 4; }} class DoublePointsCalculator extends RewardPointsCalculator { getPoints(transactionValue: number) { var standardPoints = super.getPoints(transactionValue); return standardPoints * 2; }} var pointsCalculator = new DoublePointsCalculator(); alert(pointsCalculator.getPoints(100.99));

21

The Liskov Substitution Prin-ciple

개체 타입을 검사해서 진행되는 코드의 경우 LSP 를 위반할 가능성이 높음

완벽하게 대체 가능하게 하기 위해서 고려해야할 사항

( 멤버의 제한자 , 함수의 인자 타입 , 함수의 리턴 타입 , 오류 발생시 예외 타입 )

리스코프 치환 원칙

S 가 T 의 하위속성이라면 프로그램의 변경없이T 의 객체를 S 로 교체 ( 치환 ) 할 수 있어야 한다 .

Data Abstraction and Hireachy, 1998 - Babara Liskov

22

The Liskov Substitution Prin-ciple 계약 규칙

서브타입에서 더 강력한 사전 조건을 정의할 수 없다 . 서브타입에서 더 완화된 사후 조건을 정의할 수 없다 . 슈퍼타입의 불변식 ( 항상 탐으로 유지되는 조건들 ) 은 서브타입에서도 반드시 유지되어야 한다 .

가변성 규칙 서브타입의 메서드 인수는 반 공변성 (contravariance) 을 가져야 한다 . 서브타입의 리턴 타입은 공변성 (variance) 을 가져야 한다 . 서브타입은 슈퍼타입이 발생시크는 것돠 동일한 타입의 예외나

그 서브타입이나 슈퍼타입만 사용해야 한다 .

출처 : C# 으로 배우는 적응형 코드

23

The Interface Segregation Principle

클래스가 인터페이스 구현시 필요하지 않은 기능에 대해서 구현할 필요가 없어야 함

이미 구현된 인터페이스에 새로운 기능을 추가할 경우 기존에 해당 인터페이스를

구현한 클래스들을 모두 수정해야 함

인터페이스 분리 원칙

인터페이스를 최대한 작게 만드는 것이여러가지 기능을 하는 인터페이스 하나보다 더 좋다 .

24

ex. Printer interface

interface Printer { copyDocument(); printDocument(document: Document); stapleDocument(document: Document, tray: number);}

interface Printer { printDocument(document: Document);}interface Stapler { stapleDocument(document: Document, tray: number);}interface Copier { copyDocument();}class SimplePrinter implements Printer { printDocument(document: Document) { //... }}class SuperPrinter implements Printer, Stapler, Copier { printDocument(document: Document) { //... } copyDocument() { //... } stapleDocument(document: Document, tray: number) { //... }}

25

The Dependency Inversion Principle

컴퍼넌트들 끼리 강하게 결합되어 있는 경우 수정이 힘듬

이미 구현된 클래스들 조차 재사용성이 힘들어짐

의존성 역주입 원칙

직접적인 의존을 피하고 ,인터페이스에 의존하라 .

26

ex. High-level dependency on low-level class class LightSwitch { private isOn = false; constructor(private light: Light) { } onPress() { if (this.isOn) { this.light.switchOff(); this.isOn = false; } else { this.light.switchOn(); this.isOn = true; } }}

interface LightSource { switchOn(); switchOff();} class Light { switchOn() { //... } switchOff() { //... }}

Design Pattern

28

Design Pattern 이미 알려진 문제에 대해 해결책을 디자인을 통해서 해결

GoF(gang of four) 의 Design Patterns 가 가장 유명함

(23 가지 패턴 )

Diaz and Harmes 의 Pro JavaScript Design Patterns 가 있음

Strategy Pattern, Abstract Factory Pattern 만 알아보겠음

29

The Strategy Pattern

알고리즘을 캡슐화

런타임에 인터페이스를 구현한 알고리즘을 제공

전략 패턴

30

The Abstract Factory Pattern

Factory Pattern : 객체를 직접 생성하지 않고 , Factory 를 통해서 생성

Abstract Factory : Factory 를 추상화 . 인터페이스로 구현한 Factory 를 런타임에

전달

추상 팩토리 패턴

31

ex. Wheel cleaning and BodyCleaninginterface WheelCleaning { cleanWheels(): void;} class BasicWheelCleaning implements WheelCleaning { cleanWheels() { console.log('Soaping Wheel'); console.log('Brushing wheel'); }} class ExecutiveWheelCleaning extends BasicWheelCleaning { cleanWheels() { super.cleanWheels(); console.log('Waxing Wheel'); console.log('Rinsing Wheel'); }}

interface BodyCleaning { cleanBody(): void;} class BasicBodyCleaning implements BodyCleaning { cleanBody() { console.log('Soaping car'); console.log('Rinsing Car'); }} class ExecutiveBodyCleaning extends BasicBodyCleaning { cleanBody() { super.cleanBody(); console.log('Waxing car'); console.log('Blow drying car'); }}

32

ex. CarWashProgram class before the ab-stract factory patternclass CarWashProgram {

constructor(private washLevel: number) { } runWash() { var wheelWash: WheelCleaning; var bodyWash: BodyCleaning; switch (this.washLevel) { case 1: wheelWash = new BasicWheelCleaning(); wheelWash.cleanWheels(); bodyWash = new BasicBodyCleaning(); bodyWash.cleanBody(); break; case 2: wheelWash = new BasicWheelCleaning(); wheelWash.cleanWheels(); bodyWash = new ExecutiveBodyCleaning(); bodyWash.cleanBody(); break;

case 3: wheelWash = new ExecutiveWheelCleaning(); wheelWash.cleanWheels(); bodyWash = new ExecutiveBodyCleaning(); bodyWash.cleanBody(); break; } }}

33

ex. Abstract factory and Concrete factories

interface ValetFactory { getWheelCleaning() : WheelCleaning; getBodyCleaning() : BodyCleaning;}

 class SilverWashFactory implements ValetFactory { getWheelCleaning() { return new BasicWheelCleaning(); } getBodyCleaning() { return new ExecutiveBodyCleaning(); }} class GoldWashFactory implements ValetFactory { getWheelCleaning() { return new ExecutiveWheelCleaning(); } getBodyCleaning() { return new ExecutiveBodyCleaning(); }}

class BronzeWashFactory implements ValetFactory { getWheelCleaning() { return new BasicWheelCleaning(); } getBodyCleaning() { return new BasicBodyCleaning(); }}

34

ex. Abstract factory pattern in use

class CarWashProgram { constructor(private cleaningFactory: ValetFactory) { } runWash() { var wheelWash = this.cleaningFactory.getWheelCleaning(); wheelWash.cleanWheels(); var bodyWash = this.cleaningFactory.getBodyCleaning(); bodyWash.cleanBody(); }}

MixinsSteve’s Ice Cream in Somerville, Massachusetts.

36

TypeScript Mixins

step 1. 클래스 내의 mix-in 함수를 선언

step 2. Augumented class ( 증강 클래스 ) 를 implements 로

전달받음

(extends 가 아님 )step 3. 일반 클래스와 똑같이 해당 함수를 사용

37

ex. Mixin enabler function

function applyMixins(derivedCtor: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { if (name !== 'constructor') { derivedCtor.prototype[name] = baseCtor.prototype[name]; } }) });}

38

ex. Reusable classesclass Sings { sing() { console.log('Singing'); }} class Dances { dance() { console.log('Dancing'); }} class Acts { act() { console.log('Acting'); }}

39

ex. Composing classesclass Actor implements Acts { act: () => void;} applyMixins(Actor, [Acts]); class AllRounder implements Acts, Dances, Sings { act: () => void; dance: () => void; sing: () => void;} applyMixins(AllRounder, [Acts, Dances, Sings]);

40

ex. Using the classes

var actor = new Actor();actor.act(); var allRounder = new AllRounder();allRounder.act();allRounder.dance();allRounder.sing();

41

When to use Mixins 상속 (is a) 도… ..

위임 (has a) 도……

안될 때 !!! -> 믹스인 (can do)

추가 옵션을 가질 수 있는 클래스 ( 믹스인 함수 )

여러 클래스에서 동일한 함수를 재사용

비슷한 기능들의 조합으로 여러 클래스를 생성

42

Restrictions of Mixins 믹스인 함수를 private 으로 선언하면 안됨

증강 클래스의 함수를 private 로 선언하면 안됨

증강 클래스의 변수는 매핑 안됨 디폴트 값을 정의하지 않는 것이 좋음

개체 별로 종속되지 않는 경우에는 static 으로 선언하는 방법도 고려

개체 별로 다른 값이 필요한 경우 믹스인 함수에서 초기화가 필요

43

ex. Properties not mappedclass Acts { public message = 'Acting'; act() { console.log(this.message); }} class Actor implements Acts { public message: string; act: () => void;} applyMixins(Actor, [Acts]); var actor = new Actor(); // Logs 'undefined', not 'Acting'actor.act();

class Acts { public static message = 'Acting'; act() { alert(Acts.message); }}

Summary

45

Summary

TypeScript 는 객체지향적인 특징들을 대부분 포함하고 있습니다 .

SOLID 이론은 코드가 계속 유지될 수 있도록 해주는 것을 그 목표로 하고 있습니다 .

디자인 패턴은 일반적으로 알려진 문제들에 대한 해법이 될 수 있습니다 .

믹스인은 각각의 요소들을 대체할 수 있는 방법을 제공합니다 .