TS 3일차

Fizz·2022년 10월 2일
0

회사일이 바빠서 ㅠㅠ
평일에 JS공부랑 알고리즘 풀고, 회사일을 조금 하니 시간이 없었다...
회사에서 TS를 안쓰니 우선순위가 밀리는 것 같다.
그래서 주말에 하려고 한다!

객체지향

객체들을 컨셉으로 프로그래밍을 해나가는 방식.

프로그램을 객체로 정의해, 객체들끼리 소통하도록 디자인.

한곳에서 문제가 생기면 관련 Obj만 수정, 재사용 가능!
확장성도 새 Obj를 만들면 되므로 좋다.
-> 생산성, 높은퀄리티, Faster

객체(object)란 간단히 이야기하자면 실생활에서 우리가 인식할 수 있는 사물로 설명할 수 있다.

이러한 객체의 상태(state)와 행동(behavior)을 구체화하는 형태의 프로그래밍이 바로 객체 지향 프로그래밍.
Error Exception 같이 조금 추상적인 것도 Obj 가능 -> 명사형으로 이름!

Class

이때 객체를 만들어 내기 위한 설계도와 같은 개념을 클래스(class)라고 한다.

클래스는 객체의 상태를 나타내는 필드(field)와 객체의 행동을 나타내는 메소드(method)로 구성된다.

즉, 필드(field)란 클래스에 포함된 변수(variable)를 의한다.

메소드(method)란 어떠한 특정 작업을 수행하기 위한 명령문의 집합.
클래스에 데이터를 넣어서 Obj인스턴스를 만든다!

type Coffeecup = {
	shots: number;
    hasMilk: boolean;
}

Class CoffeeMachine {
	static BEANS_PER_SHOT: number = 5; // class lv
    coffeBeans: number = 0; // instance lv 
    
    constructor(coffeeBeans: number) {
    	this.coffeeBeans = coffeeBeans
    }
    
    static makeMachine(coffeeBeans: number): CoffeeMachine {
    	return new CoffeeMachine(coffeeBeans
    }
    
    makeCoffee(shots: number): Coffeecup {
    	if (this.coffeeBeans < shots * CoffeeMachine.BEANS_PER_SHOT) {
        	throw new Error();
        }
        
        this.coffeeBeans -= shots * CoffeeMachine.BEANS_PER_SHOT;
        
        return {
        	shots,
            hasMilk: false
        }
    }
}

const maker = new CoffeeMachine(33);
const maker2 = CoffeeMachine.makeMachine(3);

객체지향 원칙

1. 캡술화(Encapsulation)

  • 연관된 데이터들을 하나의 Obj안에 모아두는 것 외부에서 보일 필요 없는 내부데이터를 숨긴다. 내부 상태를 변경하지 않고 어떤 외부 데이터가 들어 오느냐에 따라 상태가 변하게 한다.
  • static : 함수나 변수 앞에 static사용시 class레벨로 설정
    즉 만들고 있는 클래스에 고정된 데이터가 된다.
    static을 사용하지 않으면 instance레벨로 외부 값을 주입함에 따라 변경이 가능하다. stattic은 클래스를 따로 만들지 않고 함수를 바로 호출 가능하다.
  • private, protected : public은 기본적으로 자동 생략이 된다.
    그 데이터에 접근 가능하다는 뜻이다.
    private는 외부에서 보이지않게, 접근하지 못하게 만드는 것이다.
    protected는 private 보다 조금 더 허용해 준다. 상속받았을 경우 허용을 해준다.
    public - 어디서든(생략 가능)
    protected - 상속관계만
    private - 해당 클래스에만 만약 private static을 동시에 사용하면 해당 클래스 내부에만 사용가능해진다.
  • getter
  • setter 위의 예제에서는
    marker.coffeeBeans = -100 같이 외부에서 수정가능하다. 이를 피할 수 있다.
type Coffeecup = {
	shots: number;
    hasMilk: boolean;
}

Class CoffeeMachine {
	private static BEANS_PER_SHOT: number = 5; // class lv
    private coffeBeans: number = 0; // instance lv 
    
    private constructor(coffeeBeans: number) {
    	this.coffeeBeans = coffeeBeans
    }
    
    static makeMachine(coffeeBeans: number): CoffeeMachine {
    	return new CoffeeMachine(coffeeBeans)
    }
    
    fillCoffeeBeans(beans: number) {
    	if (beans < 0) {
        	throw new Error();
        }
        this.coffeeBeans += beans;
    }
    
    makeCoffee(shots: number): Coffeecup {
    	if (this.coffeeBeans < shots * CoffeeMachine.BEANS_PER_SHOT) {
        	throw new Error();
        }
        
        this.coffeeBeans -= shots * CoffeeMachine.BEANS_PER_SHOT;
        
        return {
        	shots,
            hasMilk: false
        }
    }
}

const maker = new CoffeeMachine(33);
maker.fillCoffeeBeans(33);


class User {
    get fullName(): string{
		return `&{fistName}${lastName}`;
    }
    private internalAge = 4;
    get age(): number {
    	return this.internalAge;
    }
    set age(num:number) {
    	this.internalAge = num;
    }
    constructor(private firstName: stinrg, private lastName: string) {
    }
    // 이러면 firstname, lastname으로 설정되고 this의 , private로 설정된다!
}

user.age = 6; 

2. Abstraction(추상화)

어떤 Obj에 대한 내부구조를 알 필요없이 사용하도록 하는 것.

  • encaptulation
  • interface - implements
    클래스에서 함수를 생성하고, 다른사람이 그 코드를 볼때 어떤 함수를 사용할지 혼란스러울때 interface를 사용하여 추상화를 구상할 수 있다.
    interface의 뜻은 접속하다이며, 특정함수를 선택해 그것만 사용하게 하는것이다.
    implements는 선언한 클래스 옆에 선언한 ㅑnterface이름을 적어주면 된다.
type Coffeecup = {
	shots: number;
    hasMilk: boolean;
}

interfact CoffeeMaker {
	makeCoffee(shots: number): CoffeeCupt;
}

interfact CommercialCoffeeMaker {
	makeCoffee(shots: number): CoffeeCupt;
    fillCoffeeBeans(shots: number): void;
    clean(): void;
}

Class CoffeeMachine implements CoffeeMaker, CommercialCoffeeMaker{
	private static BEANS_PER_SHOT: number = 5; // class lv
    private coffeBeans: number = 0; // instance lv 
    
    private constructor(coffeeBeans: number) {
    	this.coffeeBeans = coffeeBeans
    }
    
    static makeMachine(coffeeBeans: number): CoffeeMachine {
    	return new CoffeeMachine(coffeeBeans)
    }
    
    clean() {
    	console.log('cleaning');
    }
    
    private fillCoffeeBeans(beans: number) {
    	if (beans < 0) {
        	throw new Error();
        }
        this.coffeeBeans += beans;
    }
    
    private preheat(): void {
    	console.log('heat');
    }
    
    private extract(shots:number): CoffeeCup {
    	return { 
        	shots,
            isHeat: false
            }
    }
    
    grindBeans(shots: number) {
    	if (this.coffeeBeans < shots * CoffeeMachine.BEANS_PER_SHOT) {
        	throw new Error();
        }
        
        this.coffeeBeans -= shots * CoffeeMachine.BEANS_PER_SHOT;
    }
    
    makeCoffee(shots: number): Coffeecup {
    	this.grindBeans(shots);
        this.preheat();
        
        return this.extract(shots);
    }
}

const maker: CoffeeMachine = CoffeeMachine.makeMachine(33);
const maker2: CommercialCoffeeMaker = CoffeeMachine.makeMachine(33); // 인터페이스 따름
maker.fillCoffeeBeans(33);

3. Inheritance (상속)

기존 Obj를 활용하는것을 상속이라 한다. -> 재사용성 good

  • extends : 상속을 위해 사용한다. 부모 클래스에서 private을 사용하면 상속이 되지 않으니 public이나 protected로 바꾸어 주어야 한다.

  • super : 자식클래스에서 super. 으로 가져와 사용할 수 있다.

type Coffeecup = {
	shots: number;
    hasMilk: boolean;
}

interfact CoffeeMaker {
	makeCoffee(shots: number): CoffeeCupt;
}

Class CoffeeMachine implements CoffeeMaker{
	 static BEANS_PER_SHOT: number = 5; // class lv
     coffeBeans: number = 0; // instance lv 
    
     constructor(coffeeBeans: number) {
    	this.coffeeBeans = coffeeBeans
    }
    
    static makeMachine(coffeeBeans: number): CoffeeMachine {
    	return new CoffeeMachine(coffeeBeans)
    }
    
    clean() {
    	console.log('cleaning');
    }
    
     fillCoffeeBeans(beans: number) {
    	if (beans < 0) {
        	throw new Error();
        }
        this.coffeeBeans += beans;
    }
    
     preheat(): void {
    	console.log('heat');
    }
    
     extract(shots:number): CoffeeCup {
    	return { 
        	shots,
            isHeat: false
            }
    }
    
    grindBeans(shots: number) {
    	if (this.coffeeBeans < shots * CoffeeMachine.BEANS_PER_SHOT) {
        	throw new Error();
        }
        
        this.coffeeBeans -= shots * CoffeeMachine.BEANS_PER_SHOT;
    }
    
    makeCoffee(shots: number): Coffeecup {
    	this.grindBeans(shots);
        this.preheat();
        
        return this.extract(shots);
    }
}

class CaffeLatteMachine extends CoffeeMachine {  --> 생성자가 private일떄느 안됨

	constructor(beans: number, public readonly serialNumber: string) {
    	super(number)
    } -> 자식 클래스의 생성자는 꼭 super호출
	private steamMilk(): void {
    }
	makeCoffee(shots:number): CoffeeCup {
    	const coffee = super.makeCoffee(shots) //부모 함수호출
        this.steamMilk();
        
    	return {
        	...coffee,
            hasMilk: true,
        }
    }
}

const maker: CoffeeMachine = CoffeeMachine.makeMachine(33);
maker.fillCoffeeBeans(33);

4. Polymorphism(다양성)

type Coffeecup = {
	shots: number;
    hasMilk?: boolean;
    hasSugar?: boolean;
}

interfact CoffeeMaker {
	makeCoffee(shots: number): CoffeeCupt;
}

Class CoffeeMachine implements CoffeeMaker{
	 static BEANS_PER_SHOT: number = 5; // class lv
     coffeBeans: number = 0; // instance lv 
    
     constructor(coffeeBeans: number) {
    	this.coffeeBeans = coffeeBeans
    }
    
    static makeMachine(coffeeBeans: number): CoffeeMachine {
    	return new CoffeeMachine(coffeeBeans)
    }
    
    clean() {
    	console.log('cleaning');
    }
    
     fillCoffeeBeans(beans: number) {
    	if (beans < 0) {
        	throw new Error();
        }
        this.coffeeBeans += beans;
    }
    
     preheat(): void {
    	console.log('heat');
    }
    
     extract(shots:number): CoffeeCup {
    	return { 
        	shots,
            isHeat: false
            }
    }
    
    grindBeans(shots: number) {
    	if (this.coffeeBeans < shots * CoffeeMachine.BEANS_PER_SHOT) {
        	throw new Error();
        }
        
        this.coffeeBeans -= shots * CoffeeMachine.BEANS_PER_SHOT;
    }
    
    makeCoffee(shots: number): Coffeecup {
    	this.grindBeans(shots);
        this.preheat();
        
        return this.extract(shots);
    }
}

class CaffeLatteMachine extends CoffeeMachine {  --> 생성자가 private일떄느 안됨

	constructor(beans: number, public readonly serialNumber: string) {
    	super(number)
    } -> 자식 클래스의 생성자는 꼭 super호출
	private steamMilk(): void {
    }
	makeCoffee(shots:number): CoffeeCup {
    	const coffee = super.makeCoffee(shots) //부모 함수호출
        this.steamMilk();
        
    	return {
        	...coffee,
            hasMilk: true,
        }
    }
}

class SweetCoffeeMaker extends CoffeMachine {
    
    makeCoffee(shots: number) :CoffeeCup {
    	const coffee = super.makeCoffee(shots);
        return {
        	...coffee,
            hasSugar: true,
        }
    }
} -> 다양성 한 클래스로 다양한 클래스가 나옴, 

const maker: CoffeeMachine = CoffeeMachine.makeMachine(33);
maker.fillCoffeeBeans(33);

const machine: CoffeeMaker[] = [
	new CoffeeMachine(6),
    new CaffeeLatteeMachine(6, '1'),
    new SweetCoffeeMaker(15),
    new CoffeeMachine(12),
] - > Inplements 를 상속받아 인터페이스  배열
	makeCoffee 만 호출 가능 (interface)
machine.forEach((machine) => {
	machine.makeCoffee(1)
})

CoffeeMachine 한 클래스로 라떼, 단 커피 등등 여러가지를 만들 수 있다.

내부적으로 구현된 다양한 클래스들이 한 인터페이스 혹은 동일한 부모를 받았을 때, 구별않고 다양한 기능이지만 공통된 API호출이 가능하다. , 가독성 및 추상화 증대

상속의 문제

관계가 복잡해진다.
부모의 행동을 수정하면 부모를 상속하는 모든 곳에 영향을 미친다.
또한 TS에서는 한가지 이상의 부모를 상속하지 못한다.
ex 달달한 카페라떼 머신은 뭘 상속해야 할까, 흑설탕은?
커피머신 > 카페라떼 머신(우유), 달달한 머신(설탕) ->

해결 > composition

profile
성장하고싶은 개발자

0개의 댓글