코드스피츠 - 객체지향 자바스크립트

밍디·2022년 7월 30일
0

https://www.youtube.com/watch?v=E9NZ0YEZrYU

중요하게 살펴보아야하는 점 value vs indentifier

value context(메모리 주소와 관계없이 값이 같으면 같은 것)

vs

identifier context(메모리 주소가 다르면 값이 같아도 다른 것)

객체지향은 indentifier context로 개발해야하고, 함수형은 value context 기반으로 개발해야함

    const a = {
      a: 3,
      b: 5,
    };

    const b = {
      a: 3,
      b: 5,
    };
    console.log(a === b); //false (1)
	console.log(JSON.stringfy(a) === JSON.stringfy(b));//true (2)

(1)이 indentifier context의 대표적인 예
(2)가 value context의 예 -> 값 비교를 하는 순간 객체지향은 깨졌다고 보면 된다.

여기서 질문
Q. 그러면 단순히 숫자를 받아오는 경우에는 값으로 받아야할텐데 이럴땐 어떻게 객체지향을 쓰는가?
A. new Count 3 이런식으로 받는 것
-> 생성자에서 받아와야하네,,

value context 특징

  1. 끝 없는 복사본
  • 3을 할당한 값을 끝없이 복사해서 넘어가 사용한다.
  1. 상태 변화에 안전
  • 한번만들어지면 안변하니까 안전해보이는 것
  • 3+1을 하면 3,1은 그대로 있는데 4가 생성되는 원리라 안전한 것
    -> 함수형 프로그래밍 등장한 이유 : 새로운 값을 토하지않고 사용하려고
  1. 연산을 기반으로 로직을 전개
    -> 문제 : 복잡한 형태(도메인)를 표현하는 연산을 만드는 것이 어려워 수학적 프로그래밍은 비추천

indentifier context 특징

  1. 하나의 원본
  • 값의 상태가 계속 변화, 상태가 일관성이 없음
  1. 상태 변화를 내부에서 책임짐 , 내부 상태 변화를 책임지는 객체를 만들어야함
  2. 메세지를 기반으로 로직을 전개
  • 연산이 아닌 메세지를 통해 로직을 전개시켜야함, 내가 할것만하고 다음건 다른 곳으로 위임하면됨

Polymorphisom (다형성)


const Worker = class{
  run(){
    console.log("working")
  }
  print(){
    this.run();
  }
};
const HardWorker = class extends Worker{
  run(){//오버라이딩
    console.log("hardWorking")
  }
};
const worker = new HardWorker();
console.log(worker instanceof Worker);//true -> 자식은 부모를 대체할 수 있다.
worker.print();
  
  • 다형성의 대체가능성 : 자식(확장된 클래스)은 부모(대상 클래스)를 대체할 수 있지만, 부모는 자식을 대체할 수 없다.

  • 내적 일관성 : 어떤 경우에도 태어났을때의 원본 클래스를 유지하려는 성질

즉, 다형성은 대체가능성 + 내적 일관성

worker.print()의 결과는? worker가 HardWorker로 태어났기 때문에 this는 HardWorker라서 hardWorking으로 출력하기로 합의한 것이 객체지향점의 특징

Polymorphism of Prototye

  • 언어마다 대체가능성과 내적일관성을 구현하는 방식은 다름

  • js는 프로토타입을 사용

  • 프로토타입 체인 : 본인 먼저 찾아보고 계속 타고가서 찾음. 상속받을때, 가장

A instanceof B
worker instanceof HardWorker 가능
worker instanceof Worker 가능 (타고타고 올라가니까)

즉 자바스크립트는 프로토타입을 통해 대체가능성, 내적일관성을 유지.
각 언어마다 이 두가지를 어떤 것을 통하든 유지하려고 노력함

자바스크립트는 객체지향 언어인가? YES. polymorphism 성립하기 때문에

하지만, 객체지향의 특성을 띄게 코딩을 하는 것은 다른 문제이다.

Object essentials

const EssentialObject = class{
  #name = "";         //hide state
  #screen = null;     //hide state
  constructor(name){
    this.#name = name;
  }
  camouflage(){
    this.#screen = (Math.random() * 10).toString(16).replace(".", "")
  }
  get name(){
    return this.#screen || this.#name; //encapsulation
  }
};
  • 떳떳히 내 이름을 보여줄 거냐 (name) 위장을 할거냐(screen)을 선택할 수 있는 구조
  1. 객체지향에서 특징 : 속성은 private. 모든 속성이 외부에 노출되지 않는다.

상세 이유 : 객체지향은 메모리의 참조로 움직여야하는데 속성이 공개되는 순간 속성을 값으로 취득하게되고, 값으로 쓰게되면 값 컨텍스트가 만연해지고 객체지향이 무너짐. private가 아닌 변수가 있다면 객체지향이 아님

  1. 객체지향의 특징 : 메소드는 반드시 캡슐화되어있는 상태로 제공되어야함
  • 캡슐화 : 바깥에서 내부에서 뭘하고 있는지 모른다. 즉 내 이름(name)을 보여주는건지 위장된 이름(screen)을 보여주는지 모르는 것

1+2 => Isolation of change. 변화에 대한 격리를 이룰 수 있다.

객체지향의 필수 원칙

SOLID 원칙 - 마틴 파를로?

  • SRP(Single Responisibilit) 단일책임의 원칙
    : 고쳐야하는 경우가 하나뿐인 것
    : 이것을 하지 못할 경우 - 이곳저곳 문제가 생겨버림, 테스트 코드가 촘촘히 있다는 것은 결고 좋은 것이 아님
  • OCP(Open Closed) 개방폐쇄 원칙
    : open(class extend)가 가능하고, closed(설계시 기존에 문제를 해결했던 것을 건드리지 말고 새로운 문제가 있을경우 새로운 문제를 해결해라(extend를 받아))
  • LSP(liskov substitusion) 업캐스팅 안전 원칙
    : 추상층의 정의가 너무 구체적이면 구상층의 구현에서 모순이 발생함
문제예시수정후

생물을 숨을 쉰다, 다리로 이동한다로 추상해버리면 고래, 독수리는 해당되지 못함. 업캐스팅을 못하게됨 -> -> 인터페이스를 두개로 분리한다. -> ISP

  • ISP (Interface Sepregation) 인터페이스 분리
    : 객체의 변화가 각각의 모듈과 그 모듈에 관련된 인터페이스만 영향이 가도록 분리시키는 것

  • 예시 : 객체는 집이고 A,B,C는 가족 구성원으로 하나의 상태인 돈을 A,B,C가 사용한다. A,B,C 는 서로 전혀 다른 행위를 함에도 객체는 메소드를 6개나 갖고있을 수 밖에 없는데, 그 이유는 돈은 하나이기 때문. 이게 문제가 되는 것은 객체가 수정이되면 A,B,C 모두에게 영향이 간다.

  • 해결책1 : 분신을 만든다. 상태를 사용할 수 있을 뿐더라 분리까지 가능하다. 인터페이스를 사용하지 않고 해결하는 방법. 위임을 사용한다!

  • 해결책2 : 인터페이스를 사용하는 방법. 처음부터 인터페이스를 3개로 분리하여 객체를 as 아빠, as 엄마로 두어 사용한다.

  • DIP(Dependency Inversion) : 다운캐스팅금지
    : ex if this instance of ChildA
    Q. 다운캐스팅은 어떻게 안할수있나?
    A. 다운캐스팅을 안하는 것은 SOLID 원칙이 모두 지켜졌을때 가능하기 때문에 매우 어려움

추가로 알아야할 원칙

Dependency Injection 의존성 주입(IoC Inversion of Control 제어역전)
DRY Don't Repeat Yourself 중복방지
Hollywood Principle 의존성 부패방지
  • 물어보지말고 요청해라 즉, action을 처리할 사람이 거꾸로 하게끔 ex) 시간날때 나한테 ~해줘 -> 격리를 깨지 않기 위한 것
Law of demeter 최소 지식
  • 최소한의 지식만 가지고 메소드/필드를 가져야함
  • 의존성 역전 예시
    Manager에서 worker를 검사할때 new Worker로 해서 Worker를 상속받는 아이들이 바뀌더라도 Manager 코드는 수정할필요가 없게되고(close)
    Worker의 상속은 계속해서 이루어질 수 있으니 open되어있음
const Worker = class{
  run(){
    console.log("working")
  }
  print(){
    this.run();
  }
};
const HardWorker = class extends Worker{
  run({
    console.log("hardWorking")
  }
};

const Manager = class{
  #workers;
  constructor(...workers) {
    if(workers.every(w=>w instanceof Worker)) this.#workers = workers;
    else throw "invalid workers";
  }
  doWork(){
    this.#workers.forEach(w=>w.run())
  }  
};
const manager = new Manager(new Worker(), new HardWorker());
manager.doWork();

Inversion of COntrol

profile
노후를 위해 꾸준히 공부하자.

0개의 댓글