86 객체지향 자바스크립트 1회차

이누의 벨로그·2022년 3월 20일
0

코드스피츠 86 객체지향 자바스크립트 - 1회차

이번 시간에 우리는 객체지향의 이론들을 학습할 것입니다.

추상 메소드나 인터페이스를 상속받는 이유는 오퍼레이션과 메소드를 분리하여 런타임에 동적 바인딩하기 위함. 결국 OCP를 만들어내게 됨

의존성

  • 상속

상속받은 객체는 부모객체와 아주 강력하게 결합된다. 부모객체가 변화하면 상속받은 객체들도 영향을 받으며, 상속관계를 해제할 수도 없다.

  • 연관

해당 객체의 필드의 상대방 객체를 알고 있는 것. 필드에 알게된 멤버 객체는 클래스의 인스턴스가 생성되어 소멸할 때까지 계속해서 해당 객체와 연관되게 된다.

💡 위의 두 가지는 객체의 라이프사이클동안 영구적으로 연결되어 진다. 관계된 객체의 변화가 매우 크리티컬하게 영향을 끼친다.
  • 의존

오퍼레이션 실행시에만 연결되는 경우. 메소드 내에서 생성되거나 인자로 들어오는 객체는 메소드 호출 시에만 의존성이 생기고 없어진다.

우리는 되도록이면 강한 관계에서 약한 관계로 의존성을 옮기고 싶다.


의존성이 높으면

  1. 수정여파의 규모가 증가한다.
  2. 수정하기 힘든 구조가 생성된다.
  3. 순환 의존성의 존재 - 직접 의존성이 없더라도 간접적으로 서로 같은 객체에 의존성이 생기면 순환적으로 의존성이 존재하게 됨
💡 객체지향을 배우는 이유는 객체지향의 방법론을 동원해서 격리구간을 세우고 의존성을 관리하기 위해서. 의존성을 관리하는 이유는 변화에 대한 격리를 하기 위해서
  • 의존성 역전

어떠한 경우에도 다운캐스팅을 금지한다. 그리고 어떤객체를 인식할 때 항상 추상 인터페이스를 사용한다.

OCP를 달성하게 되면 자연스럽게 의존성 역전이 달성된다. 의존성이 구상 클래스가 아니라 추상 클래스에 존재하게 되면 의존성 역전이 달성되며, 이는 자연스럽게 추상클래스로부터의 확장에는 열려있고, 추상클래스를 인식하는 코드는 변화에는 닫혀있게 된다.(Open Close Principle)

제어 역전 (IOC)

SOLID원칙/ 의존성 역전 등으로 궁극적으로 도달하여야 할 목표는 제어 역전(Inversion Of Control)이다.

제어 역전에서 제어란 프로그램의 흐름 통제를 말한다. 동기 흐름 제어 / 비동기 흐름 제어 등

예시로, 훌륭한 택시 운전수에게 운전을 맡긴다면 (위임) 제어는 택시 운전사가 대신하지만 외부에서는 내가 운전하는 것으로 인식. 타기만 하더라도 뛰어난 운전자가 된다. 목적지를 얘기하거나 요금을 지불하는 등의 행위만으로 제어를 위임하는 것.

제어 흐름이 어려운 이유는 제어 흐름과 상태 통제를 동시에 해야하기 때문. 이러한 알고리즘을 짜는 것은 어려운 일. 사고를 훈련시켜서 가능해 진다고 하더라도 변화해 취약하며 재현하기도 어렵다.

제어문을 짜기도 힘들 뿐더러 제어문을 유지보수하기도 어렵다.

이에 대한 대안:

  1. 제어를 추상화하고
  2. 개별 제어의 차이점만 외부에서 주입받는다.

2번의 이유는 제어를 한 번만 구현하고, 제어를 일반화 하여 개별 제어를 연역적으로 추리하고 귀납적으로 나머지 개별 제어를 연역적으로 처리할 수 있게 된다.

현상으로 부터 원리를 도출한다 - 연역적 추리

연역적 사고로 도출한 원리를 현상에 적용한다. - 귀납적 사고

현상으로 부터 원리를 깨닫고 원리를 다시 현상에 적용하는 연역적/귀납적 사고를 한번에 일으키는 것을 추론이라고 한다.

따라서 일반화한 제어를 구현한 뒤에 차이점만 외부에서 공급받도록 하는 추론이 필요하다.

const Renderer = class{
	#view = null; #base = null;
	constructor(baseElement){
	 this.#base = baseElement
	}
	set view(v){
		if(v instancof View)this.#view = v;
		else throw `invalid view :${v}`
	}
	render(data){
		const base = this.#base, view = this.#view;
		if(!base|| !view) throw 'no base or view';
		let target = base.firstElementChild;
		do base.removeChild(target);while(target = target.nextElementSibling); //base의 모든 자식 제거
		base.appendChild(view.getElement(data));
		view.initAni();
		view.startAni();
	}
}
const View = class{
	getElement(data){throw 'override!';}
	initAni(){throw "override!";}
	startAni(){throw "override!";}
}

제어가 개별 view마다 존재하지 않고 Renderer클래스의 render메소드에 집중되어 있다. 모든 View객체는 render에 개별 제어의 차이점만 공급해주면 된다.(추상화된 인터페이스를 통하던지 - 전략 객체 패턴 , 상속을 통하던지- 템플릿 메소드 패턴)

const renderer = new Renderer(document.body);
renderer.view = new class extends View{ //익명 클래스
    #el;
    getElement(data){
        this.#el = document.createElement('div');
        this.#el.innerHTML = `<h2>${data.title}</h2><p>${data.description}</p>`;
        this.#el.cssText = `width:100%; background:${data.background}`;
        return this.#el;
    }
    initAni(){
        const style = this.#el.style;
        style.marginLeft= '100%';
        style.transition = "all 0.3s";
    }
    startAni(){
        requestAnimationFrame(_=>this.#el.style.marginLeft = 0);
    }
}
profile
inudevlog.com으로 이전해용

0개의 댓글