디자인 패턴

from Android 2013. 8. 1. 11:14

*주의 : 아래의 코드는 어디까지나 이해를 돕기 위한 코드이므로 실제 런은 불가능함;

반복적 코드 사용에 대한 재활용 편의를 높이고 복잡한 코드를 쉽게 관리하기 위해 디자인 패턴을 사용한다.

몇가지 유형과 예를 들어보면


1.싱글턴 패턴: 


C의 전역변수랑 비슷한 개념으로 게임내에서 유일한 존재를 만들고자 할때 사용한다.

예를 들어 싱글플레이만을 지원하는 게임에서 플레이어 인스턴스가 두개 이상 생성되는것을 방지한다.

복수의 인스턴스가 생성되는것을 막기 위해 private생성자static키워드를 사용한다.

class Player {

private Player(){} //private형 생성자로 인스턴스화를 방지

public static 게임캐릭터 _캐릭터; 

private static Player _newPlayer;


public void 공격(){

_캐릭터.공격개시();

}

public void 방어(){

_캐릭터.방어개시();

}

//기존의 new를 사용하지 않고 객체를 생성해주는 메소드를 따로 생성했다.

public static Player CreateInstance(){

//return new Player <-이부분을 바로 아래에  좀 더 확장해보겠음

if(_newPlayer==null)

newPlayer = new Player;

return newPlayer;

}

}

public class {

//new 키워드 ㄴㄴ해. 생성자가 private이므로 먹히지도 않음.

Player player1 = Player.CreateInstance();

Player player2 = Player.CreateInstance(); //두번째 객체가 생성되려 할때 대입되버림. 즉, 둘 다 같은 객체임.

while(전쟁){

if(공격하려고 함)

player1.공격();

else if(방어하려고 함)

player1.방어();

}


}


2.스트래티지 패턴

if문으로 구구절절 구분하지 말고 클래스화 시켜서 구분한다. 인터페이스 등을 응용한다.

예를 들어 아래와 같은 경우 코드가 너무 복잡하다.

class 게임캐릭터 {

private String 무기;

void 공격(){

if(무기=="몽둥이"){

if(특정거리일때){

공격애니메이션("휘두르기");

나쁜놈.대미지(2)

}

}


else if(무기=="도끼"){

if(특정거리일때){

공격애니메이션("찍기");

나쁜놈.대미지(2)

}

}


else if(무기=="전기톱"){

if(특정거리일때){

공격애니메이션("갈기");

나쁜놈.대미지(2)

}

}


(생략)...그외에 낫, 대검, 각목 등 다양한 무기...

}

}

그런데 만약 여기서 무기가 더 추가되거나 삭제할 경우 브라켓{}이 잘 삭제되지 않거나 찌꺼기(?)가 남는 부작용이 생길수도 있다.


이를 방지하기 위해 각 무기들을 다음과 같이 클래스화 시킨다.

class 몽둥이 implements 무기인터페이스 {

@Override

public void 공격(게임캐릭터 _캐릭터){

_캐릭터.세팅(this);


if(특정거리일때){

공격애니메이션("휘두르기");

나쁜놈.대미지(2)

}

}

}

class 도끼 implements 무기인터페이스 {

@Override

public void 공격(게임캐릭터 _캐릭터){

_캐릭터.세팅(this);


if(특정거리일때){

공격애니메이션("찍기");

나쁜놈.대미지(10)

}

}

}

class 전기톱 implements 무기인터페이스 {

@Override

public void 공격(게임캐릭터 _캐릭터){

_캐릭터.세팅(this);


if(특정거리일때){

공격애니메이션("갈기");

나쁜놈.대미지(25)

}

}

}

..등등등 이런식으로 구분을 해준 뒤

이들을 취합할 인터페이스를 하나 만든다.

interface  무기인터페이스{

void 공격(게임캐릭터 _캐릭터); //인터페이스이므로 몸체가 없는 추상 메소드로 구현해야 한다.

}


인터페이스를 생성했다면 위의 게임캐릭터 클래스에 인터페이스를 적용시키기 위해 수정하도록 한다.

class 게임캐릭터 {

private String 무기;  --> private 무기인터페이스 무기;


게임캐릭터(){

무기 = new 전기톱(); //전기톱이 생성

}

 

void 공격(){

무기.공격(this); //if문들 싹 다 지움.

}

}

생성자에 new 키워드를 사용하여 다양한 무기 객체를 생성할 수 있다.


무기를 동적으로 변경하기 위해서 메소드등을 사용하여 유연하고 용이하게 대처할 수 있다.

//메소드 생성

public void 무기세팅(무기인터페이스 _무기){

무기 = _무기


//사용 예시

_캐릭터.무기세팅(new 몽둥이());

_캐릭터.공격();


3.스테이트 패턴

특정 상황을 플래그 클래스로 관리하는 패턴.

상황 자체를 클래스화시켜서 제어한다. 여기에도 인터페이스를 적용한다.


가령 여고생 클래스를 하나 만든다고 생각해보겠다.

class 여고생 

{

static final int 남친구함=0;

static final int 썸씽중=1;

static ifnal int 품절됨=2;


int 나이;

int 상태;

여고생(){

상태=남친구함;

나이=18;

}


public void 누굴좋아함(){

if(상태==품절됨){

 대사("이제 그 남자는 잊어야겠군. 난 쿨녀니까");

  }

....else if(생략)

}

public void 누가좋아함(){

if(상태==품절됨){

 대사("좋아하는 사람이 있지만 안알랴");

  }

......(생략)

}

public void 이학생을공략(){

if(상태==품절됨){

대사("넌 안돼")

......(생략)

}

}

 이런식으로 로직을 짜 나아갈 것이다.

그런데 이것도 일일이 if~else if로 구분하려니 코드가 너무 길어진다..

그래서 남친구함, 썸씸중, 품절됨 등과 같은 상태 정보를 클래스로 분리해서 입맞에 맞게 호출하는거다.

interface 여고생인터페이스(){

public void 누굴좋아함();

public void 누가좋아함();

public void 이학생을공략();

}

인터페이스를 작성하는 이유는 스트래티지 패턴때와 동일하게 하나의 규격을 (말하자면 전기콘센트 같은것)을 만들어놓고

여기에 여러 플러그를 꽂아서 사용하듯 우리는 상태에 대한 클래스를 여기에 맞춰서 적절한 상황에 사용할것이다.

이제 각각의 몸체를 구현하면 ok.

몸체에는 여고생 객체를 받아올 객체형 변수를 하나 선언해야 한다. 이런것도 그냥 생성자 부분에 넣어주면 편하다.

class 여고생 

{

static final int 남친구함=0;

static final int 썸씽중=1;

static ifnal int 품절됨=2; <--필요 없어진 부분들

여고생인터페이스 m_상태

여고생인터페이스 남친구함 m_남친구함;

여고생인터페이스 남친구함 m_남친구함;

여고생인터페이스 남친구함 m_남친구함; <--인터페이스형 변수들을 선언


int 나이;

int 상태;

여고생(){

m_남친구함 = new 남친구함(this);

m_썸씽중= new 썸씽중(this);

m_품절됨= new 품절됨(this);


상태=m_남친구함;

나이=18;

}

public void 상태변경(여고생인터페이스 _여고생상태){

m_상태 = 여고생상태;

}


public void 누굴좋아함(){

if(상태==품절됨){

 대사("이제 그 남자는 잊어야겠군. 난 쿨녀니까");

  }

....else if(생략)

} ↓아래처럼 수정

public 여고생인터페이스 get남친구함(){

return m_남친구함;

}

public void 누가좋아함(){

if(상태==품절됨){

 대사("좋아하는 사람이 있지만 안알랴");

  }

......(생략)

↓아래처럼 수정

public 여고생인터페이스 get썸씸중(){

return m_썸씽중;

}

public void 이학생을공략(){

if(상태==품절됨){

대사("넌 안돼")

......(생략)

} ↓아래처럼 수정

public 여고생인터페이스 get품절됨(){

return m_품절됨;

}


즉 기존에 final static의 상수형으로 선언한 상태값들을 클래스형으로 만들어서 분리했다.

이렇게하면 코드를 관리하기도 쉽고 수정이 용이한 장점이 있다.


4.팩토리 패턴

객체를 무한정 반복해서 많이 찍어내고자 할때 유용하다.

여기서 각 객체들이 복붙하듯이 전부 똑같다는 의미가 아니라

삼국무쌍, 스타같은 게임 등등 동시에 비슷한 유닛들을 동적으로 할당받고자 할 때 편리한 패턴이다.

유닛 하나하나를 전부 객체로 생성한다고 생각해보자.

class Unit 

{

int HP;

int ATK;

  int 유닛좌표x;

int 유닛좌표y;

Unit(int HP, i int ATK...등등)({

....(초기화코드 등등)

}

void 유닛세팅 {

.....유닛위치

.....유닛모양 등등..

}


void 특수기능{

}

}

class  자쿠 extends Unit{

@override

void 특수기능{

......어쩌고저쩌고한 로직

}

}

class  돔 extends Unit{

@override

void 특수기능{

......어쩌고저쩌고한 로직

}

}

약간의 차이가 있다고는 하나 공통되는 부분을 부모클래스로 만들어서 상속받은 형태로 갈 공산이 크다.

그럼 이 모든 객체를 일일이 이렇게 생성해야 맞는것인가... 안됀다. 진한 노가다 스멜이 풍긴다.

그래서 팩토리라는 이름의 클래스를 하나 생성해준다.

class  UnitFactory{

public Unit  CreateUnit(int 종류, int x, int y){

Unit _unit = null;

if(종류==1){

_unit = new 자쿠;

_unit.HP=1;

_unit.ATK=1;

}

if(종류==2){

_unit = new 돔;

_unit.HP=2;

_unit.ATK=2;

}

_unit.유닛좌표x=x;

_unit.유닛좌표y=y; //자바의 다형성을 이용. x,y 인자는 동일하게 받는다쳐도 '종류'변수가 구별해주므로 성립.

   return _unit;

}

}

이렇게 하면 유닛을 세팅할때 Unit _unit1 = UnitFactory.CreateUnit(종류,좌표x,좌표y)만 넣으면 간단히 세팅되므로

반복되는 노가다를 피할 수 있게 된다.

,