728x90

이전 코드에서 봤을때 외부에서 클래스를 봤을 때 사용할 수 있는 함수가 너무 많아 사용이 불편했다.

이를 해결해 줄 수 있는 개념이 바로 abstraction(추상화)이다.

추상화의 의의는 개발자가 코드를 보다 쉽게 이해할 수 있도록 돕는데 있다.

클래스 외부에서 사용할 함수에 네이밍과 매개변수 사용의 제한을 주어(다형성 적용시)

여러 클래스가 존재하더라도 같은 이름과 사용방법으로 같은 기능을 구현할 수 있다.

interface를 클래스에 implement하여 해당 클래스의 조상을 설정함과 동시에 사용할 함수, 변수를 제한할 수 있다.


이전코드

type Pokemon = {
  name:string;
  level: number;
};
const pokemonArr : Pokemon[] = [{name:'이상해꽃'
                                 level:50},
                                {name:'리자몽'
                                 level:50},
                                {name:'거북왕',
                                 level;50}
                               ];
type Party= {
  pokemon : integer; //파티 포켓몬수
  hasLegendary:boolean; // 전설의 포켓몬 포함여부
};

class WeakParty{
  private static MINIMUM_LEVEL_REQUIRED : number = 50;
  private pokemon : Pokemon[];
  private constructor(pokemon:readonly Pokemon[]){
    this.pokemon = pokemon;
  }

  get pokemon(): Pokemon[]:{
      return pokemon;
  }

  set pokemon(pokemon : Pokemon[]){
    this.pokemon = pokemon;
  }

  public fillPokemon(pokemon : Pokemon){
    if(pokemon == null) throw new Error("no pokemon to fill");
    this.pokemon.push(pokemon);
  }

  public makeInstance(pokemon: Pokemon[]){
    return new WeakParty(pokemon);
  }

  public makeWeakParty():Party{
    if(pokemon.length()<1){
      throw new Error("no pokemon to participate");
    }
    pokemon.forEach((item)=>{
      if(item.level<WeakParty.MINIMUM_LEVEL_REQUIRED){
        throw new Error("too low level");
      }
    })
    return{
      pokemon.length(),
      hasLegendary:false
    }
  }
}


//const myParty = new WeakParty(pokemonArr); // 이제 작동하지 않음
const myParty = WeakParty.makeWeakParty(pokemonArr);
console.log(myParty.makeWeakParty()); // {3,false}

myParty.fillPokemon({name:'피카츄'
                     level:50})
console.log(myParty.makeWeakParty); // {4,false}

일반대회는 파티의 멤버를 추가하여 수정이 가능하지만

정식 대회는 이미 파티로 선정한 포켓몬에서 추가할 수 없다.

진짜 포켓몬을 대체하여 수정을 하려면 코드가 좀 복잡해지므로 파티를 추가하는 경우만 생각하자 (fillPokemon())


추상화를 적용한 코드

{
  type Pokemon = {
    name: string;
    level: number;
  };
  const pokemonArr: Pokemon[] = [
    { name: "이상해꽃", level: 50 },
    { name: "리자몽", level: 50 },
    { name: "거북왕", level: 50 }
  ];
  type Party = {
    pokemon: number; //파티 포켓몬수
    hasLegendary: boolean; // 전설의 포켓몬 포함여부
  };

  //interface 생성
  interface Rank {
    makeParty(): Party;
    fillPokemon(pokemon: Pokemon): void;
  }

  interface Competition {
    makeParty(): Party;
  }

  // 추상 클래스 생성한
  abstract class PartyImpl implements Rank, Competition {
    constructor(protected pokemon: Pokemon[]) {}

    public fillPokemon(pokemon: Pokemon) {
      if (pokemon == null) throw new Error("no pokemon to fill");
      this.pokemon.push(pokemon);
    }
    public abstract makeParty(): Party; // 자식 클래스마다 내용이 달라질 수 있다.
  }

  class WeakParty extends PartyImpl {
    private static MINIMUM_LEVEL_REQUIRED: number = 50;
    constructor(pokemon: Pokemon[]) {
      super(pokemon);
    }
    public makeInstance(pokemon: Pokemon[]) {
      return new WeakParty(pokemon);
    }
    public makeParty(): Party {
      if (this.pokemon.length < 1) {
        throw new Error("no pokemon to participate");
      }
      this.pokemon.forEach(item => {
        if (item.level < WeakParty.MINIMUM_LEVEL_REQUIRED) {
          throw new Error("too low level");
        }
      });
      return {
        pokemon: this.pokemon.length,
        hasLegendary: false
      };
    }
  }

  const partyForRank: Rank = new WeakParty(pokemonArr.slice());
  const partyForCompetition: Competition = new WeakParty(pokemonArr.slice());

  partyForRank.fillPokemon({ name: "피카츄", level: 50 });
  console.log(partyForRank.makeParty()); //{pokemon:4,legendary:false}

  //partyForRank.fillPokemon({name:'피카츄',level:50}); //불가능
  console.log(partyForCompetition.makeParty()); //{pokemon:3,legendary:false}
}

interface

  • 구현이 되지않은 class라 생각하면 편하다. 미처 구현이 되지 못한 부분은 상속받는 class에서의 구현 내용을 따라간다.
  • 생선된 변수의 타입을 interface로 지정하면 class의 구조에 상관없이 interface내의 함수,변수만 사용할 수 있다.

implements

  • inherit과 유사한 개념으로 interface와 class를 상속관계로 맺어준다.
  • 한 class에 여러 interface를 상속시킬 수 있다.

abstract class

  • 자식 클래스에서 꼭 구현을 해야하는 함수가 있는데 사용법을 다르게 하거나 놓칠 수 있는 위험이 있는 경우 사용한다.
  • abstract class 자체로는 intance를 만들 수 없다.
  • 자식 클래스마다 달라질 수 있는 행동에 대해 abstract class 내 method 앞에 abstract를 붙인다.
    이 때 interface 처럼 해당 함수에는 구현 사항을 작성해서는 안되며 자식 클래스에서는 꼭 구현이 되어야한다.

+ Recent posts