본문 바로가기

학부_대학원

Head First Design Patterns(스토리가 있는 패턴 학습법) 1장

SMALL

기능 구현을 위한 몇줄 안되는 개발만 하다가 설계를 어떻게 공부하면 좋을지 고민

위의 책이 쉽게 이해가 된다고 하여서 해보려 한다.

기본적으로 java에 관해서 공부를 하고 듣고, 설계의 필요성을 느끼고 공부하면 좋은거 같다.

책은.. 돈이 없어서 구매를 못했다. 하지만 관련 내용을 소개한 곳이 있어서 참고하면서 공부해보려 한다.

[참고 및 출처] http://wiki.gurubee.net/pages/viewpage.action?pageId=1507368

--> 그림과 같은 내용 친절히 전부 있습니다. 

--> 공부하면서 정리하는 개념입니다.

위의 사이트 기준으로 코드를 구현해보고 문제를 해결 하는식으로 진행!!

0. 공부하기 전에..

Interface와 추상화 Class의 차이점에 관해서 정리하고 넘어가려 한다.



1. 문제에 대한 인지

위와 같이 오리라는 abstract Class Duck이 존재하고, 그 밑으로 MallarDuck과 RedDuck을 만들었다.

// Duck Class

//abstract class에서만
//abstact method를 사용 할 수 있다.
//아니면 오류가 난다.
public abstract class Duck {
	void quack(){
		System.out.println("꿱 꿱\n");
	};
	void swim(){
		System.out.println("우아하게 수영\n");
	};
	
	// 구현은 상속받은 class에서 구현해야 한다.
	// 반드시 구현해야 한다.[강제성]
	// 구현을 하지 않으면 오류가 난다.
	abstract void display();
}
//MallarDuck Class
public class MallarDuck extends Duck{

	@Override
	void display() {
		// TODO Auto-generated method stub
		System.out.println("MallarDuck 처럼 생겼다~");
	}
}

//RedDuck Class

public class RedDuck extends Duck{

	@Override
	void display() {
		// TODO Auto-generated method stub
		System.out.println("빨간 오리 처럼 생겼다~");
	}
}

위와 같이 구현을 하고 이제 확장을 시켜보도록 한다.

수영만하고 짖기만 하던 오리가 이제 날수 있는 기능을 추가하려고 한다. 

그리고 석촌호수에 존재하는 Rubber Duck을 표현하려 한다. Rubber Duck은 짖을수도 없고 날수도 없다.

위와 같은 내용을 구현해야 한다.

해당 코드를 구현 할 때 밑과 같은 문제가 발생한다고 한다.

첫번째 문제는 서브 클래스에 코드가 중복이 된다는점.
두번째 문제는 모든 오리의 행동을 알기 힘들다는점.(어떤오리가 추가 될지 어떻게 알수 있단 말인가???)
세번재 문제는 코드를 변경했을때 (또는 새로운 행동을 추가 했을때) 다른 오리들에게 원치 않는 영향을 끼칠수 있다는점.
네번째 문제는 실행시점 그러니까 오리를 만들어내는 시점에 특징을 바꾸기 힘들다는 것이다.(이건 믿에서 알아 보도록 하자)

위의 문제점이 발생한다고 하지만 잘 모르겠어서 위에서 말하는 기능을 정말 막 구현해보고 이해하려 한다.

public abstract class Duck { void quack(){ System.out.println("꿱 꿱\n"); }; void swim(){ System.out.println("우아하게 수영\n"); }; abstract void display(); //나는 기능을 추가하였다. abstract void fly(); } public class MallarDuck extends Duck{ @Override void display() { // TODO Auto-generated method stub System.out.println("MallarDuck 처럼 생겼다~"); }         

//나는 기능을 위해서 함수를 추가해주었다. @Override void fly() { // TODO Auto-generated method stub System.out.println("날수있다~"); } } public class RedDuck extends Duck{ @Override void display() { // TODO Auto-generated method stub System.out.println("빨간 오리 처럼 생겼다~"); } //나는 기능을 위해 코드를 추가해 주었다. 위의 Mallar Class와 같은 코드가 구현이 되었다.

//중복 코드가 발생하였다. @Override void fly() { // TODO Auto-generated method stub System.out.println("날수있다~"); } } public class RubberDuck extends Duck{ @Override void display() { // TODO Auto-generated method stub System.out.println("석촌호수에 살것 같은 고무 오리이다."); } @Override void fly() { // TODO Auto-generated method stub System.out.println("날수 없을것 같다."); } //원래 짖을때의 quack을 오버라이드 하였다. 비효율적이다. void quack(){ System.out.println("짖을수 없다."); } }

위와 같이 코드를 확인 하였을 때, 코드가 중복이되고 불필요하게 함수를 오버로드 해야하였다.

뿐만아니라 정말 더 기능을 추가하다 보면 말도 안되게 수정을 많이 해야 할것 같은 느낌은든다.


2. 첫번째 솔루션

위와 같이 날으는 기능과 짖는 기능의 메소드를 구성하는 interface를 구성하여서 해결하는 방법이 있다.

변화하는 부분만 전부 추출하여서 따로 구현한 것이다.

이 방법은 먼저 quackable을 보면 문제점이 존재 할 것같다.

한명의 못짖는 오리때문에 전체에 전부 해당 기능을 구현해주어야 한다. --> 한명때문에 전체에 코드를?!

뿐만 아니라 새로운 기능을 추가하면 전부다 구현해주어야 하는가???!?!?!??!!


3. 두번째 솔루션

1. 클레스에서 변화는 부분과 변화지 않는 부분을 분리

2. 전체가 아닌 일부만 날수 있거나 울수 있게 혹은 울수 없게 구현


해당 밑에 부분은 날수 있고 없고에 관하여서 인터페이스를 만들고 이를 구현한  Class이다.

//나는 행동과 관련된 Interface를 만들었다. public interface FlyBehavior { public void fly(); } //위의 Interface를 받은 FlyNoWay Class를 구현하였다. public class FlyNoWay implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("I can't fly"); } } //위의 Interface를 받은 FlyWithWings Class를 구현하였다. public class FlyWithWings implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("I'm flying"); } }


해당 밑에 부분은 짖는 부분과 관련한 인터페이스를 만들고 관련 Class를 만들었다.

 고에 관하여서 인터페이스를 만들고 이를 구현한  Class이다.

//나는 행동과 관련된 Interface를 만들었다. public interface QuackBehavior { public void quack(); } //인터페이스를 구현하였다. 퀵하고 운다. public class Squeak implements QuackBehavior{ @Override public void quack() { // TODO Auto-generated method stub System.out.println("Squeak"); } }


//인터페이스를 구현하였다. 너무 조용하다. public class MuteQuack implements QuackBehavior{ @Override public void quack() { // TODO Auto-generated method stub System.out.println("<< Silence >>"); } }


위와 같이 날고 말하고에 관련된 내용을 구현해 놓았다. 그리고 이를 사용할 Duck Class를 확인 해볼 차례이다.

public abstract class Duck {
	
	
	//Interface 변수를 선언 함으로서
	//다양한 Class에서의 내용을 사용 할 수있게 한다.
	//FlyBehavior Interface에 의해서 
	//FlyNoWay와 FlyWithWings Class가 생김
	//해당 변수에 원하는 행동을 할당하면 된다.

	FlyBehavior flyBehavior;
	QuackBehavior quackBehavior;
	

	public void performFly(){
		flyBehavior.fly();
	}
	
	public void performQuack(){
		quackBehavior.quack();
	}
	
	//위의 인터페이스 변수의 내용을 변경 시키고 싶을때 사용 한다는 개념
	//오리에 대한 행동들을 변형 시키고 싶을때 사용
	public void setFlyBehavior(FlyBehavior fb){
		 flyBehavior = fb;
	}
	
	public void setQuackBehavior(QuackBehavior qb) {
		 quackBehavior = qb;
	}
	///////////////////////////////////////////////
	void swim(){
		System.out.println("우아하게 수영\n");
	};
	
	abstract void display();
}


주석으로 이해한 내용을 설명해 놓았다. 그리고 해당 부분들이 어떻게 사용되는지 보려한다.

Mallar Duck Class를 확인해보면 좀 더 명확하게 이해가 간다.

public class MallarDuck extends Duck{

	//생성자
	public MallarDuck(){
		//여기서 Mallar Duck의 행동들이 결정된다.
		//해당 오리는 날 수있다는 내용의 class를 받음
		flyBehavior = new FlyWithWings();
		//해당 오리는 짖을수 있다는 class를 받음
		quackBehavior = new Squeak();
		
	}
	@Override
	void display() {
		// TODO Auto-generated method stub
		System.out.println("MallarDuck 처럼 생겼다~");
	}
	

}

Duck에서의 Interface 변수에 행동할 내용의 class를 받는 것이다.

해당 오리가 날게 하고 싶으면 FlyWithWings Class를 넣으면 되고, 못 날게 하고 싶으면 FlyNoWay Class를 넣으면 된다.

또한 만약에 이 Mallar 오리가 다쳐서 날 수 없다면 어떻게 할 것인가? --> 원래는 날수있는데 못나는 것이다.

Main 부분을 보면 직관적으로 이해가 가능하다.


public class Main {
	
	       public static void main(String args[]){
		System.out.println("Head Hunter!!");
		
		
		//원래의 Mallar 오리의 내용이다.
                System.out.println("멀쩡한MallarDuck");
		Duck mallard = new MallarDuck();
		mallard.performFly();
		mallard.performQuack();
		
		
		//다친 오리의 내용이다.
                System.out.println("아픈MallarDuck");
		Duck sick_mallar = new MallarDuck();
		sick_mallar.setFlyBehavior(new FlyNoWay());
		sick_mallar.performFly();
		sick_mallar.performQuack();
	}
	
	
}


그리고 출력된 내용이다.

>>출력

멀쩡한MallarDuck

I'm flying

Squeak

아픈MallarDuck

I can't fly

Squeak


여기서 새로운 오리를 추가 한다고 생각 해보자.

해당 오리는 유전자 조작과 과학 기술의 힘에 의해서 날때는 로켓을 달고 날고, 짖을 때는 "깍깍"하고 까마귀 처럼 짖는다고 한다.

그러면 해당 오리를 만들 때는 FlyBehavior Interface를 구현한 RocketFly Class를 만들고, QuackBehavior Interface를 구현한 kkakkak Class를 구현하면 된다.


그리고 Modify_Duck을 만들어 보면 될 것같다.


public class KKakQuack implements QuackBehavior{ @Override public void quack() { // TODO Auto-generated method stub System.out.println("KKAK KKAK 운다."); } } public class FlyRoket implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("로켓 추진 처럼 날아간다."); } }

public class Modify_Duck extends Duck{ Modify_Duck(){ //날으는 모습 설정 flyBehavior = new FlyRoket(); //우는거 설정 quackBehavior = new KKakQuack(); } @Override void display() { // TODO Auto-generated method stub System.out.println("로켓을 달고, 철 갑판을 두른 모습이다."); } } public class Main { public static void main(String args[]){ System.out.println("Head Hunter!!"); //원래의 Mallar 오리의 내용이다. System.out.println("멀쩡한MallarDuck"); Duck mallard = new MallarDuck(); mallard.performFly(); mallard.performQuack(); //다친 오리의 내용이다. System.out.println("아픈MallarDuck"); Duck sick_mallar = new MallarDuck(); sick_mallar.setFlyBehavior(new FlyNoWay()); sick_mallar.performFly(); sick_mallar.performQuack(); //변종 오리 Duck modify = new Modify_Duck(); System.out.println("변종 오리"); modify.display(); modify.performFly(); modify.performQuack(); } }

그리고 출력된 내용이다.

>>출력

멀쩡한MallarDuck

I'm flying

Squeak

아픈MallarDuck

I can't fly

Squeak

변종 오리

로켓을 달고, 철 갑판을 두른 모습이다.

로켓 추진 처럼 날아간다.

KKAK KKAK 운다.



LIST

'학부_대학원' 카테고리의 다른 글

객체 지향[oop] 특징 4가지  (0) 2016.11.01
C++ 사용에 대한 이해  (0) 2016.07.27
Overriding vs Overloading  (0) 2016.07.26
중앙값 정리  (0) 2016.07.24
운영체제  (0) 2016.07.22