객체지향 프로그래밍?

Jung taeWoong·2021년 4월 28일
0

typescript

목록 보기
3/11
post-thumbnail

Object Oriented Programming

Imperative and Procedural Programming

절차지향적 프로그래밍이란?

  • 정의된 순서대로 절차적으로 함수가 하나씩 호출되는 방식
  • 함수와 데이터들이 얽혀있어 전체적인 어플리케이션의 구조를 알아야함
  • Side Effect 발생 확률 높고 유지보수와 확장이 힘들다.

Object Oriented Programming

객체지향적 프로그래밍이란?

  • 객체(관련된 데이터나 코드를 묶음)들을 컨셉으로 프로그래밍을 하는 방식
  • Object 단위로 만들어나가기 때문에 한곳에서 문제가 생기면 관련있는 오브젝트만 수정하면 된다.
  • Object는 Object의 속성을 가진 데이터들과 행동을 할수있는 함수로 구성되어있다.

객체지향의 4가지 원칙

캡슐화:Encapsulation

서로 관련있는 데이터와 함수들을 한 Object에 담아두고 외부에 보일 필요없는 데이터들을 숨겨두는 것이 캡슐화

추상화:Abstraction

추상화를 통해 외부에서는 내부에 복잡한 로직들을 신경쓰지 않고 외부 인터페이스 함수를 이용해 Object를 사용하는 것

상속:Inheritance

자식 클래스가 부모 클래스의 데이터나 함수를 그대로 받아서 사용할수 있는 것
IS-A관계: 클래스들 사이의 포함 관계를 의미하며, 한 클래스 A가 다른 클래스 B의 서브 클래스임을 이야기 한다.

다형성:Polymorphism

상속을 통해 만들어진 Object들은 어떤 타입인지 상관하지 않고 공통된 함수를 통해 접근할수 있다.

Class

  • Class는 데이터가 들어있지않은 틀, 이 틀을 이용해 데이터를 넣어 만든 것이 Object
  • 하나의 Class로 다양한 Object를 만들 수 있다.
  • Class도 하나의 타입이다.
  • 관련된 속성과 함수들을 묶어서 어떤 모양의 데이터가 될것이라는걸 정의하는 것
  • Object마다 새로 만들어져야될 데이터는 멤버 변수(instance level)로 정의
  • Class level에서 함께 공유되는 것들은 static으로 정의
    - static은 멤버변수나 함수에 적용가능

static

  • 클래스 내부에 어떠한 속성값도 필요하지 않을때 static 키워드를 붙여 외부에서도 클래스를 만들지 않고도 사용할수 있게 된다.
  • JS의 Math함수처럼 따로 Object를 생성하지 않아도 class level에서 함수를 호출할수 있다.
  • class 내에서 static(class level)로 등록된 함수나 변수들은 this가 아닌 class 이름으로 접근
  • 변하지 않는 상수 값, 여러 Object(instance)에 걸쳐서 사용될 수 있는(즉, 오브젝트의 상태 데이터에 접근할 필요가 없는) 함수들에 static을 사용

abstract 추상클래스

  • 클래스 자체는 만들어지는 것을 목적에 두지 않고 필요한것을 정의해두는 것이 목적
    - 추상클래스는 생성할수 없고 구현한 클래스만 만들수 있다.
  • 자식클래스마다 달라질수있는 함수는 앞에 abstract 키워드를 붙임
    - 이런 함수는 자식클래스만 접근할수 있기에 protected 접근지정자를 지정
    • 추상적이기에 구현사항은 적지 않고 선언.
    • 이런 abstract 키워드가 붙은 함수는 상속하는 클래스에서 항상 따로 구현해주어야 함
  • abstract클래스에서 의도한대로 최대한 abstract으로 지정된 함수들만 오버라이딩 해야한다.
abstract class AbstractClass {
//... 공통된 기능들;
	
// 상속한 클래스마다 달라져야하는 기능은 abstract 함수로 생성
  protected abstract absFunc();
}

interface와 abstract class의 차이점

  • interface는 구현사항이 들어갈수 없고 속성과 행동의 타입만 정의함
  • abstract class는 공통적으로 필요한 로직을 구현할 수 있음

Encapsulation 캡슐화

  • class를 만들때 외부에서 접근할수있는 것은 무엇인지 내부적으로만 갖고있어야 되는것이 무엇인지 결정할 수 있다.
  • 내부에 상태를 private로 숨기고 외부의 함수를 통해 상태를 변경할 수 있게 코드를 작성

정보 은닉 방법

  • public: 외부에서 접근가능 (dfeault)
  • private: 외부에서 접근불가
  • protected: 외부에서 접근할 수없지만 이 클래스를 상속한 자식 클래스에게만 접근가능
/* static 키워드가 붙은 Object를 만들수있는 함수를 제공한다면 
생성자함수로 만드는것을 금지한다는말과 같다. 
constructor()에 private로 만들어서 항상 static 함수를 사용할수있도록 권장 */

class TestClass {
  pricate data: number = 0;
  prviate constructor(data: number) {
    this.data = data;
  }

  static makeClass(data: number) {
    return new TestClass(data);
  }
}

Getter, Setter

  • getter, setter은 일반 변수처럼 사용가능하지만 어떠한 계산을 해야할때 유용하게 사용가능
  • 전달된 데이터의 유효성검사를 할 수 있다.
class User {
  private internalAge = 5;
  get age(): number {
    return this.internalAge;
  }
  set age(num: number) {
    if (num < 0) {
      throw new Error('is not valid age'); // 유효성 검사
    }
    this.internalAge = num;
  }
}

const user = new User();
user.age = 6; // setter 호출
console.log(user.age); // 6 

Abstraction 추상화

  • 인터페이스를 간단하게 만듦으로써 사용자가 많은 생각을하지 않고도 사용가능하게 도와준다.
  • 접근제어자 encapsulationinterface를 통해 추상화가 가능하다.

encapsulation으로 추상화 방법

  • 접근제어자를 통해 함수를 관리하여 정말 필요한 함수만 노출하여 클래스를 사용하기 쉽게 만듬
class CoffeeMachine {
  private grindeBenas(shots: number) {
    ...logic
    
  };
  private preheat(): void {
    ...logic
    
  };
    
  private extract(shots: number) {
    ...logic
  };
  makeCoffee(sthos: number) {
     this.grindBeans(shots);
     this.preheat();
    
     return this.extract(shots);
  }
}

const coffee = new CoffeeMachine();
coffee.grindBeans() // error 찾지못함
coffee.preheat() // error 
coffee.extract() // error
coffee.makeCoffee(2);

interface로 추상화 방법

  • interface란 요구사항을 명시해놓은 계약서와 같다.
  • interface를 구현하는 class에서는 interface에 규약된 모든 함수를 구현해야한다.
  • interface를 이용하면 내가 얼마만큼의 행동을 허용할것인지 결정할 수 있다.
    - class보다 좁은 범위의 interface에 규약된 함수들만 접근 가능
  • class에 여러 interface를 지정할 수 있다.
  • 사용자는 class에 다른 복잡한 기능을 알필요 없고 interface만 어떻게 사용하면 되는지만 알면 된다.
interface Contract {
  otherFunc(num: number): otherObj;
}

class otherClass implements Contract {
  otherFunc(num: number): otherObj {
    ...logic
  }
  
  otherOtherFunc(str: string): void {
    ...logic
  }
}

const class1: otherClass = new otherClass();
class1.otherFunc(2);
class1.otherOtherFunc('123');

const class2: Contract = new otherClass(); // interface로 타입을 제한
class2.otherFunc(2);
class2.otherOtherFunc('123'); // error

Inheritance 상속

  • 부모 클래스에서 정의된 변수와 함수를 자식 클래스가 물려받는 것
  • 부모 클래스의 멤버(변수, 함수)를 재사용함으로써 자식 클래스가 간결해진다.
  • 클래스간 계층적 분류 및 관리가 쉬워진다.
  • 상속하려는 부모클래스의 생성자 함수가 public이나 protected 이어야 상속이 가능
class Child extends Parent {
  constructor(...) {
    super(...); // Parent Class 생성자함수
  }
  
  // Parent Class 함수 와 데이터 이용 가능
}

Polymorphism 다형성

  • 한가지 class나 interface를 통해서 다른 방식으로 구현한 class를 구현할 수 있다.
  • 내부적으로 구현된 다양한 class들이 한가지 interface를 구현하거나 동일한 Parentclass를 상속했을때 동일한 함수를 어떤 class인지 구분하지 않고도 공통된 api를 호출할 수 있다.
  • 하나의 interface나 Parent class를 상속한 Child class들이 interface와 Parent class의 함수를 다른 방식으로 다양하게 구현함으로써 다형성을 만듬
  • 동일한 api 함수를 통해 각각 구현된 내부 구현사항을 신경쓰지않고 약속된 한가지 api를 호출함으로써 사용자도 간편하게 다양한 기능들을 활용할수 있다.
class Child1 extends Parent {
    commonFunc(num: number): void {
      super.commonFunc(num);
      console.log('child func');
    }
  }

const childs: Parent[] = [
    new Child1,
    new Child2,
    new Child3,
  ];

childs.forEach(child => {
  console.log('----------------');
  child.commonFunc(2);
})

상속의 문제점

  • 상속은 수직적으로 관계가 형성됨 (부모 -> 자식)
  • 부모클래스를 수정하게되면 그것을 상속하는 모든 클래스에 영향을 끼침
  • 한가지 이상의 클래스를 상속할수 없음

Composition

  • 상속의 복잡한 수직적인 관계를 피할수있고 상속의 레벨을 한 단계로 유지하고 필요한 코드를 재사용할수 있다.
  • 각각의 클래스에서 매번 필요한것을 구현하는 것이 아닌 외부에서 만들어진 각각의 기능별로 클래스를 만들어 둠으로써 필요한곳에서 가져다 쓰는 것
  • 필요한 기능을 가져와서 외부에서 주입받음으로써 composition을 이용해서 필요한 기능을 재사용함

문제점

  • classclass들 사이의 관계를 짓는것은 좋지 않다.
  • composition class를 수정하면 그와 관련된 모든 class들을 수정해야 한다.
  • class들 사이의 서로 상호작용하는 경우에 class 자신을 노출하는 것이 아닌 interface를 통해 상호작용을 해야 한다.
profile
Front-End 😲

0개의 댓글