Programming/Java

11. 패턴을 통한 객체지향 언어의 이해

주죵 2021. 1. 10. 21:52
728x90
반응형

패턴이란?

패턴(디자인패턴)은 객체지향 언어를 배우는데 중요한 방법이다!

어떻게하면 빠르게 개발하고 효율적으로 유지 보수 할 수 있을까? 이러한 고민을 하던 중 객체지향의 상속, 인터페이스, 추상화등의 기능으로 시간과 인력투입의 효율을 극대화시키기 위한 방법이 정립되기 시작했다. 그리오 이러한 방식을 어떠한 틀로 정해 놓은 것이 바로 '디자인 패턴'이다.

쉽게 정리하면 객체지향의 장점만을 모아 효율적이고 빠르게 개발할 수 있도록 만들어놓은 틀이 패턴이라고 할 수 있다.

다양한 패턴을 습득하고 나면 왜 상속, 추상화등이 필요한지 이해할 수 있게될 것이다. 패턴은 아주 다양한 종류가 있다.  디자인 패턴 관련 학습은 꾸준히 하는것이 좋다.

 

간단한 패턴 몇가지를 살펴보도록 하자.

 

어떤 클래스는 오로지 하나의 객체만 만들수 있다. 그 하나의 객체를 많은곳에서 공유해서 사용한다. 즉, 전역의 개념으로 객체를 사용한다.

예시코드를 살펴보자

 

package com.javalex.singleton;

public class SingletonClass {
	//본인 객체 생성. 자기가 속한 클래스에서 나온 객체의 메모리(Heap영역) 주소값을 메모리에(데이터 영역, Class메모리) 가지고 있다.
	private static SingletonClass SINGLETON_CLASS_INSTANCE = new SingletonClass();
	public int i = 10;
	
	
	//private인 생성자. 외부클래스에선 객체 생성이 불가능.
	//싱글턴의 특성인 하나의 인스턴스를 내부에서만 생성
	private SingletonClass() {
		// TODO Auto-generated constructor stub
	}

	//외부에서의 접근은 내부에 생성된 객체에 public 메소드를 통해 접근
	//public + static : 객체가 생성되기 전에 데이터 영역에 있는 Class상태에서 접근 가능
	public static SingletonClass getSingletonClass() {
		if (SINGLETON_CLASS_INSTANCE ==null) {
			SINGLETON_CLASS_INSTANCE = new SingletonClass();
		}
		return SINGLETON_CLASS_INSTANCE;
		
	}
	
	public int getI() {
		return i;
	}
	public void setI(int i) {
		this.i = i;
	}

}

package com.javalex.singleton;

public class FirstClass {
	public FirstClass() {
		// TODO Auto-generated constructor stub
	
	
	//private 생성자이므로 객체 생성이 불가하기 때문에 get메소드로 접근.
	SingletonClass singletonClass = SingletonClass.getSingletonClass();
	System.out.println("FirstClass");
	System.out.println("i="+singletonClass.getI());
	singletonClass.setI(200);
	System.out.println("new i="+singletonClass.getI());
	}
}

package com.javalex.singleton;

public class SecondClass {
	public SecondClass() {
		// TODO Auto-generated constructor stub
		SingletonClass singletonclass = SingletonClass.getSingletonClass();
		System.out.println("SecondClass");
		System.out.println("i="+singletonclass.getI());
		
	
	}

}

package com.javalex.singleton;

public class MainClass {
	
	public static void main(String[] args) {
		// TODO Auto-generated constructor stub
		FirstClass first = new FirstClass();
		SecondClass second = new SecondClass();
		
	}

}



 

메모리 관련해 기억이 안나는 부분을 짚고 넘어가보자면, 

메모리는 데이터영역 / heap영역 두가지로 구분되는데, 우리가 사용하는 클래스는 데이터영역, 클래스로 부터 만들어진 객체는 Heap영역으로 저장된다. 이때 SingletonClass에서 사용되는 static 메소드는 Class영역에 존재하므로 객체가 생성되기 전에 클래스의 메모리 안에 존재한다.(객체가 생성되기 전에 사용 가능)

위의 예시에선 객체가 생성되기 전이면(Null인경우)엔 새로운 객체를 생성하고, 생성이 되어있다면 만들어진 객체로 접근하도록 설정되어있다.  

 

결과적으로 main 메소드에서 실행되는 부분을 보면, 첫번째 class의 생성자에 의해 SingletonClass에서 하나의 객체가 생성된다. 맨 처음에 설정된 static한 변수 i는 10이었으나, FirstClass에서 200으로 재설정된다. 

SecondClass에 의해 다시 SingletonClass의 객체로 접근하는데, 이때 접근한 객체는 이전 FirstClass에서 생성한 객체와 동일하다. 따라서 출력시에 앞서 바꿨던 i값 200을 그대로 출력한다

FirstClass
i=10
new i=200
SecondClass
i=200

 

따라서 위와 같은 결과가 나오게 된다. 

 

전략(Strategy) 패턴

알고리즘 군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘 변경이 가능하다.

쉽게 말하면 어떤 객체를 만들때 객체가 가지는 기능(메소드)가 다양하게 존재할 것이다. 이러한 메소드를 추성화하여 언제든지 적용할 수 있게 만드는 것이다. 즉 기능을 부품화하는 것이다.

제조시스템공학에서 자동차를 만들때 늘 금형의 예를 들며 표준화, 모듈화를 강조했었다. 그런걸 생각하면 된다. 금형의 경우도 소형, 중형, 대형이 있을텐데 제네시스 만들땐 대형을, 아반떼를 만들땐 소형을 쓸것이다. 하지만 모두 같은 기능(금형)을 부품화시킨 구조란 것을 알 수 있다. 이와 같은 전략을 프로그래밍에 적용한것이 스트래티지 패턴이라고 보면 된다.

예제를 통해 구현해보도록 하자. 살펴볼 예제의 시나리오는 다음과 같다.

1) 로봇모양은 팔, 다리, 머리, 몸통으로 구성

2) 모든 로봇은 기본적으로 걷고 달린다

3) Super로봇은 날 수있고, 미사일을 쏘고,레이저검도 가지고있다.

4) Standard로봇은 날수는 없지만 미사일은 쏘고, 레이저검대신 목검이 있다.

5) Low로봇은 날수도 없고, 미사일도 못쓰고 검도 없다.


첫번째 간단한 구현은 다음과 같다

package com.javalex.strategy;

public abstract class Robot {
	public Robot() {
		// TODO Auto-generated constructor stub
	}
	//공통부분에 대하여 super class 상속받도록
	public void actionWalk() {
		// TODO Auto-generated method stub
		System.out.println("달릴 수 있다");

	}
	
	public void actionRun() {
		// TODO Auto-generated method stub
		System.out.println("걸을 수 있다");

	}
	
	public void shape() {
		// TODO Auto-generated method stub
		System.out.println("팔, 다리, 몸통, 머리");

	}
	
	//공통기능이지만 서로 다른 기능을 가질때 추상화
	public abstract void actionFly();
	
	public abstract void actionMisail();
	
	public abstract void actionKnife();
	

}

 

SuperClass가 되는 Robot클래스이다. 공통점을 갖는 걷기, 뛰기, 구성요소(Shape)는 구현을 한상태로 상속한다. 하지만 모두에게 적용되긴 해도 서로 다른 특징을 지닌 날기, 미사일, 검에 대한 기능은 추상화한다.

 

public class SuperRobot extends Robot{
	public SuperRobot() {
		// TODO Auto-generated constructor stub
	}
	
		
	public void actionFly() {
		// TODO Auto-generated method stub
		System.out.println("날 수 있다");

	}
	
	public void actionMisail() {
		// TODO Auto-generated method stub
		System.out.println("미사일 쏠 수 있다");

	}
	
	public void actionKnife() {
		// TODO Auto-generated method stub
		System.out.println("레이저검");

	}
	


}

public class StandardRobot extends Robot{
	public StandardRobot() {
		// TODO Auto-generated constructor stub
	}
	
	public void actionFly() {
		// TODO Auto-generated method stub
		System.out.println("날 수 없다");

	}
	
	public void actionMisail() {
		// TODO Auto-generated method stub
		System.out.println("미사일 쏠 수 있다");

	}
	
	public void actionKnife() {
		// TODO Auto-generated method stub
		System.out.println("목검");

	}
	
}

public class LowRobot extends Robot{
	public LowRobot() {
		// TODO Auto-generated constructor stub
	}
	
	public void actionFly() {
		// TODO Auto-generated method stub
		System.out.println("날 수 없다");

	}
	
	public void actionMisail() {
		// TODO Auto-generated method stub
		System.out.println("미사일 쏠 수 없다");

	}
	
	public void actionKnife() {
		// TODO Auto-generated method stub
		System.out.println("검도 없다");

	}
	



}

 

각각의 로봇들을 클래스로 구현하였다. 모든 로봇은 상속받을때 구현해야할 날기, 미사일, 검에 대한 추상메소드를 오버라이드한다.

 

public class MainClass {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Robot robot1 = new SuperRobot();
		Robot robot2 = new StandardRobot();
		Robot robot3 = new LowRobot();
		
		Robot[] roblist = {robot1,robot2,robot3};
		
		for(int i=0 ; i<roblist.length; i++) {
			roblist[i].actionFly();
			roblist[i].actionMisail();
			roblist[i].actionKnife();
			System.out.println("*********************");
		}

	}

}

 

구현을 위한 메인클래스이다. 서로 다른 생성자이지만, 상속받은 super 클래스 Robot으로 타입을 통일시키고 리스트화 하여 구현한다. 


더 정돈된 코드를 만들기 위해 인터페이스 또한 적용해보자. 날기, 미사일, 검에 관한 기능들을 인터페이스화 하여 새로운 패키지를 추가해보도록 하자.

 

인터페이스 노가다시작..

package com.javalex.robotinter;

public interface iFly {
	
	public void fly();

}


public class FlyYes implements iFly {

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("날수있다");

	}

}
public class FlyNo implements iFly {

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("날수 없다");
	}

}
public interface iKnife {
	public void knife();

}
public class KnifeLaser implements iKnife {

	@Override
	public void knife() {
		// TODO Auto-generated method stub
		System.out.println("레이저검");
	}

}
public class KnifeWood implements iKnife {

	@Override
	public void knife() {
		// TODO Auto-generated method stub
		System.out.println("목검");
	}

}
public class KnifeNo implements iKnife {

	@Override
	public void knife() {
		// TODO Auto-generated method stub
		System.out.println("검 없음");

	}

}
public interface iMisail {
	public void Misail();

}
public class MisailYes implements iMisail {

	@Override
	public void Misail() {
		// TODO Auto-generated method stub
		System.out.println("미사일 있음");
	}

}
public class MisailNo implements iMisail {

	@Override
	public void Misail() {
		// TODO Auto-generated method stub
		System.out.println("미사일없음");
	}

}

 

각 기능에 대한 인터페이스를 만들고, 기능별 클래스를 모두 구현하였다. 이제 이렇게 구현된 클래스들을 import하여 각 로봇에 적용시켜보도록 하자.

 

package com.javalex.strategy;

import com.javalex.robotinter.iFly;
import com.javalex.robotinter.iKnife;
import com.javalex.robotinter.iMisail;


public abstract class Robot {
	iFly fly;
	iMisail misail;
	iKnife knife;

	public Robot() {
		// TODO Auto-generated constructor stub
	}
	//공통부분에 대하여 super class 상속받도록
	public void actionWalk() {
		// TODO Auto-generated method stub
		System.out.println("달릴 수 있다");

	}
	
	public void actionRun() {
		// TODO Auto-generated method stub
		System.out.println("걸을 수 있다");

	}
	
	public abstract void shape();

	public void setFly(iFly fly) {
		this.fly = fly;
	}
	
	public void setMisail(iMisail misail) {
		this.misail = misail;
	}
	
	public void setKnife(iKnife knife) {
		this.knife = knife;
	}
	
	public void actionKnife() {
		// TODO Auto-generated method stub
		this.knife.knife();

	}
	
	public void actionMisail() {
		// TODO Auto-generated method stub
		this.misail.Misail();

	}
	
	public void actionFly() {
		this.fly.fly();
	}

}


 

Robot에 위에서 구현했던 인터페이스 타입의 변수를 생성하고 setter를 추가해준다.

 

package com.javalex.strategy;

public class SuperRobot extends Robot{
	public SuperRobot() {
		// TODO Auto-generated constructor stub
	}
	@Override
	public void shape() {
		// TODO Auto-generated method stub
		System.out.println("고급 로봇입니다");
	}


}

public class StandardRobot extends Robot{

	public StandardRobot() {
		// TODO Auto-generated constructor stub
	}
	@Override
	public void shape() {
		// TODO Auto-generated method stub
		System.out.println("스탠다드 로봇입니다");
		
	}
	
}

public class LowRobot extends Robot{

	public LowRobot() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void shape() {
		// TODO Auto-generated method stub
		System.out.println("하급로봇입니다");
	}

}

 

로봇별 클래스가 아주 간단해졌다. shape만 추상화 시켜놨으므로 오버라이드 해준다.

 

package com.javalex.strategy;

import com.javalex.robotinter.FlyNo;
import com.javalex.robotinter.FlyYes;
import com.javalex.robotinter.KnifeLaser;
import com.javalex.robotinter.KnifeNo;
import com.javalex.robotinter.KnifeWood;
import com.javalex.robotinter.MisailNo;
import com.javalex.robotinter.MisailYes;

public class MainClass {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Robot robot1 = new SuperRobot();
		Robot robot2 = new StandardRobot();
		Robot robot3 = new LowRobot();
		
		System.out.println("고객요청 : 고급로봇 \n************");
		
		robot1.shape();
		robot1.setFly(new FlyYes());
		robot1.actionFly();
		robot1.setMisail(new MisailYes());
		robot1.actionMisail();
		robot1.setKnife(new KnifeLaser());
		robot1.actionKnife();
		
		

	}

}

 

마지막 메인클래스를 살펴보자. 위와 동일하게 세가지 로봇에 대한 객체를 생성하는데 타입은 Super Class인 Robot으로 통일시켰다. 눈여겨봐야할점은 action 메소드들을 실행하기 위해 행해지는 set메소드 들이다. 

요청한 고급로봇과 일치하도록 MisailYes, KnifeLaser, FlyYes의 객체로 파라미터를 맞춰주었다.

이러한 구성의 가장 큰 장점은 메인메소드에서 내가 원하는 대로 객체의 기능을 바꿀 수 있다는 것이다. 이렇게 기능을 인터페이스를 통해 따로 구현해놓으면 SuperRobot생성자임에도 Fly, Misail, Knife관련 기능을 set 메소드를 통해 모두 적용시킬 수 있다. 이렇게 기능을 캡슐화, 모듈화 해놓는것을 통해 다양한 객체와 기능의 구현이 가능한 것이다.

이와 같이 기능을 모듈화, 캡슐화하여 구현하는 디자인 패턴을 스트래티지패턴이라고 한다.

 

출처 : 신입SW 인력을 위한 실전 자바 (by 블스)

 

 

728x90

'Programming > Java' 카테고리의 다른 글

13. 예외처리  (0) 2021.01.13
12. API  (0) 2021.01.11
10. 인터페이스  (0) 2021.01.07
9. 추상클래스  (0) 2021.01.07
8. 상속  (0) 2021.01.04