Chapter 1. 디자인 패턴 소개
최초설계
Duck[꽥(), 수영(), 표시()] <--- 오리1 [표시()]
<--- 오리2 [표시()]날기 추가 : 오리1, 오리2도 날수 있게 됨
Duck[꽥(), 수영(), 표시(), 날기()] <--- 오리1 [표시()]
<--- 오리2 [표시()]고무로된 오리도 날수 있게됨 : 문제발생
Duck[꽥(), 수영(), 표시(), 날기()] <--- 오리1 [표시()]
<--- 오리2 [표시()]
<--- 고무오리 [꽥(//삑삑으로 오버라이드), 표시()]고무로된 오리도 날수 있게됨 : 고무오리의 날기()를 오버라이드로 처리하여 못날게 함
Duck[꽥(), 수영(), 표시(), 날기()] <--- 오리1 [표시()]
<--- 오리2 [표시()]
<--- 고무오리 [꽥(//삑삑으로 오버라이드), 표시(), 날기(//아무것도 안함)]나무오리 : 꽥을 아무것도 안함으로 오버라이드
Duck[꽥(), 수영(), 표시(), 날기()] <--- 오리1 [표시()]
<--- 오리2 [표시()]
<--- 고무오리 [꽥(//삑삑으로 오버라이드), 표시(), 날기(//아무것도 안함)]
<--- 나무오리 [꽥(//아무것도 안함), 표시(), 날기(//아무것도 안함)]매번 오버라이드로 뻘짓하기 싫어져서 인터페이스를 사용하기로 함
날수있음[날기()] <--- 오리1 [표시(), 날기(), 꽥()]
꽥꽥거릴수 있음[꽥()] <--- 오리2 [표시(), 날기(), 꽥()]
오리[수영(), 표시(), 기타오리관련메소드()] <--- 고무오리 [표시(), 꽥()]
<--- 나무오리 [표시()]변화
날기()를 고쳐라 요구사항 들어옴 -> '날수있음'을 상속받은 모든 오리를 고쳐야 -> 오류발생 높아질 수도 있다.
Application에서
달라지는 부분과
달라지지 않는 부분을 (요놈을 뽑아서(리팩토링) 캡슐화하면 요 부분만 고치거나 확장가능하다.)
분리해라
바뀌는 부분과 안 바뀌는 부분 분리
바뀌는 부분 : 날기(), 꽥() --> {꽥(), 삑(), 뽕()} 처럼 관련된 클래스 집합을 만듬
안 바뀌는 부분 : 표시() 구현이 아닌 인터페이스에 맞춰 프로그래밍 한다.
위의 각 클래스 집합을 인터페이스로 만들어봄
날기행동_인터페이스[날기()] <--- 날개로날기[날기()]
<--- 못날기[날기(//아무것도 안함)]다형성을 위해 추상수퍼클래스로도 사용 가능
동물_추상[소리냄()] <--- 강아지[소리냄(){멍()} 멍(){짖음}]
<--- 고양이[소리냄(){뮤()} 뮤(){울음}]개 만들기 #1 개 만들기 #2 개 만들기 #3
Dog d = new Dog(); Animal animal = new Dog(); a = getAnimal();
d.멍(); animal.소리냄(); a.소리냄();
실제 구현
...."A는 B이다" 보다 "A에는 B가 있다"
구성을 사용함으로써 유연성을 크게 향상 시킬 수 있음
상속보다는 구성(composition)을 사용하라
드디어 첫번째 패턴
스트래티지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.
디자인 패턴 필요성
- 개발자간 서로 사용하는 패턴 용어
- 패턴을 이용하면 간단한 단어로 많은 것을 얘기할 수 있음 : 이번건은 스트래티지 패턴으로 할꺼야
- 디자인에 더 집중할 수 있음 : 구현 생각하느라 논점이 빗나가지 않음
- 전문용어 사용으로 개발팀의 능력 향상 : 오해의 소지가 줄고 빠른 작업 가능
- 신참개발자에게 자극
참고자료 : 다형성(polymorphism)
살짝 길어 질수도 있는 문제인데요.
최대한 간단히 대답하도록 노력하겠습니다.
먼저 다형성이라는게 먼지 알아 보겠습니다.
다형성이란 주어진 타입의 변수 하나가 여러 타입의 객체를 참조하는데 사용되고,
변수가 참조하는 객체의 타입에 맞는 메소드를 자동으로 호출할 수 있는 능력.
즉 다형성이란 특징을 사용하면 특정한 메소드 하나를 호출하면 호출이 적용되는 객체의 타입에 따라서
서로 다르게 작동하도록 만들수 있다는 이야기가 됩니다.
예를들어서 동물들을 예로 들어 보겠습니다.
class Dog{
void bark(){
System.out.println("개소리");
}
}
class Cat{
void bark(){
System.out.println("고양이");
}
}
class Lion{
void bark(){
System.out.println("사자소리");
}
}
이러한 클래스를 가지고 각각을 호출하기 위한 메소드를 만들겠습니다.
class Animal{
public static void main(String args[]){
Dog d = new Dog();
Cat c = new Cat();
Lion l = new Lion();
d.bark();
c.bark();
l.bark();
}
}
이렇게 작성이되면 각각의 동물소리를 부를수가 있습니다.
여기에 한가지를 추가 하겠습니다.
바로 동물들을 치료하는 수의사를 추가해볼께요.
수의사는 동물에게 주사를 놓을때 동물 소리가 난다고 가정을 할게요.
class Doctor{
void Dog_shot(){
System.out.println("개소리");
}
void Cat_shot(){
System.out.println("고양이소리");
}
void Lion_shot(){
System.out.println("사자소리");
}
}
이렇게 작성하면 되겠죠..
그리고 테스트 하는곳에 추가를 해보겠습니다.
class Animal{
public static void main(String args[]){
Dog d = new Dog();
Cat c = new Cat();
Lion l = new Lion();
Doctor doc = new Doctor();
doc.Dog_shot();
doc.Cat_shot();
doc.Lion_shot();
//이렇게 각각의 동물들에게 주사를 하려면 이부분을 추가를 해야 합니다.
d.bark();
c.bark();
l.bark();
}
}
하지만 여기서 문제점을 살펴 봅시다.
동물들이 추가가 된다면 어떻게 되죠?
당연히 Doctor클래스에 메소드를 추가해야 한다는 애기가 됩니다.
동물이 수십마리가 추가되면 추가되는 메소드 또한 수십개가 되며, 프로그래머로써는
엄청난 삽질(?)이 될수 도 있습니다.
이를 편리하게 하기 위해 다형성을 사용하겠습니다.
이를 위한 상위 클래스를 하나 만들게요. 클래스로 해도 되고 인터페이스로도 해도 되지요.
전 간단히 클래스로 작성하겠습니다.
abstract class Ani{
void bark();
}
이렇게 만들어 두시구요 각각의 동물클래스는 이제 위에 만들 클래스를 상속받게 합니다.
class Dog extends Ani{
void bark(){
System.out.println("개소리");
}
}
class Cat extends Ani{
void bark(){
System.out.println("고양이");
}
}
class Lion extends Ani{
void bark(){
System.out.println("사자소리");
}
}
이렇게 하시면.. 코드양만 많아 지는건 아니냐 하시겠지만 다음 Doctor클래스를 만들때 엄청난
노가다에서 벗어 나실수 있습니다.
class Doctor{
void shot(Ani a){
a.bark();
}
}
이렇게 해놓으시면 이제 main함수를 다시 수정하겠습니다.
class Animal{
public static void main(String args[]){
Ani d = new Dog();
Ani c = new Cat();
Ani l = new Lion();
Doctor doc = new Doctor();
doc.shot(d);
doc.shot(c);
doc.shot(l);
// 이러한 식으로 수정을 하시면됩니다.
// 도대체 이게 머가 좋은거냐....
// 이제 동물을 수십마리 추가를 하셔도 위에서 만든 상위클래스 Ani클래스만 상속 받으시면
// Doctor클래스에는 메소드 추가없이(노가다없이?) 사용이 가능하다는 이말입니다.
d.bark();
c.bark();
l.bark();
}
}
도움이 되셧는지 모르겠네요.
출처 : 지식인
참고자료 : 일반클래스, 추상클래스, 인터페이스
일반클래스
public class TestAbstract {
public void func1() {
System.out.println("일반 method");
}
public void func2() {
System.out.println("일반 method");
}
}
보통 클래스는 위처럼, method의 선언부와 구현부분으로 구성되어지죠
메소드의 선언부는 public void func1(), public void func2() 가되구요.
구현부분의 fun1(), fun2()메소드안의 내용이 됩니다.
추상클래스 (abstract)
public abstract class TestAbstract {
public abstract void func1() ;
public void func2() {
System.out.println("일반 method");
}
}
위의 예제에서 func1()은 좀 이상하게 생겼죠? 다시 말해 method의 prototype (선언부)만 있지 실제 method body는 없다(이런 method를 가리켜 abstract method라고 한다). 또한 이 같은 method를 만들 때 abstract란 키워드가 사용되구요.
이 같은 abstract method를 하나라도 가지고 있는 class는 abstract class로 정의해야 합니다. 이런 클래스가 추상클래스죠.
인터페이스 (Interface)
public interface Test {
public static final int XXX = 7;
public int YYY = 8;
public void func1();
public void func2();
}
바로 전에 abstract class에 대해서 설명했죠? 이것은 abstract method를 가지고 있는 class이다. 그런데 abstract class는 abstract method가 아닌, 즉 body를 가지고 있는 일반 method도 가질 수 있다.
그런데, interface라는 것은 일종의 abstract class인데, 이것이 가지고 있는 method들은 모두가 반드시 abstract method들이어야 한다. 다시 말해서 abstract method만 가지고 있는 abstract class가 interface인 것입니다.
그리고, 추상클래스나 인터페이스는
Test tObject = new Test() 와 같은 형식으로 객체를 생성할수 없죠.
일반클래스는 가능하지만..
추상클래스나 인터페이스는 그클래스를 직접적으로 사용하는것이아니라,
class Child implements Test {
public void func1() {
System.out.println(XXX);
}
public void func2() {
System.out.println(YYY);
}
}
처럼, 인터페이스나, 추상클래스를 상속받은 클래스를 사용하게 되죠.
Test tObject = new Child();
Child cObject = new Child();
위 두개모두가 가능합니다.
출처 : 지식인
|
추상클래스 |
인터페이스 |
|
클래스이다. |
추상클래스의 일종이다. |
|
하나 이상의 추상메소드를 가진다. public abstract class TAbstract { public abstract void func1() ; public void func2() { System.out.println("func2"); } } |
모든 메소드가 추상메소드이다. public interface TInterface { public static int num = 8; public void func1(); public void func2(); } |
|
상속 받을 때 extends를 사용 |
상속받을 때 implements 사용 |
|
단일 상속만 가능 |
다중 상속 가능 |
|
추상클래스를 상속받은 클래스에서 상속해준 클래스의 추상메소드를 모두 구현해야 한다. |
인터페이스를 구현한 클래스에서는 반드시 인터페이스내의 메소드를 구현한다. |
출처 : 디벨로퍼 짱
쉬어가기
내가 진정 중요하게 생각하는 것들 : 김창준 (애자일 컨설팅 대표)
http://www.ibm.com/developerworks/kr/library/dwclm/20080226/
테러리스트에서 버그까지 : 김창준 (애자일 컨설팅 대표)
http://www.ibm.com/developerworks/kr/library/dwclm/20080422/
Chapter 2. 옵져버 패턴
기상 모니터링 Application 개발 의뢰
온도/습도/압력센서 --- 기상스테이션 <--- WetherData객체 ---> 디스플레이 장비(현재상태, 통계, 예보)
확장가능하도록 API 도 만들것WetherData 클래스
WetherData[getTemperature(), getHumidity(), getPressure(), mesurementsChange()]
↖ 온도getter ↖습도getter ↖기압getter ↖기상관측값이 갱신될때마다 알려주는 메소드첫번째 대충 만든 코드
public class WeatherData {
// 인스턴스 변수 선언...
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// 기타 메소드...
}위 코드의 문제점은?
- parameter 가 맨날 똑같다.. 공통 부분
- 9~11줄은 바뀔 수 있는 부분 -> 캡슐화 하자 (스트래티지 패턴)
- 9~11줄은 구체적인 구현이므로 다른 디스플레이를 위해서는 코드를 고치는 수밖에 없다.
옵져버 패턴이란?
- 옵져버 패턴 = 출판사(subject) + 구독자(observer)
- 구독 / 해지 가능
- subject 객체는 데이타가 달라지면 observer 객체에게 값을 전달
한 객체의 상태가 바뀌면
그 객체에 의존하는 다른 객체들에게 연락이 가고 자동으로 내용이 갱신되는
ont-to-many 의존성을 정의함
옵저버 패턴 : 클래스 다이어그램

느슨한 결합
느슨한 결합(Loose Coupling) : subject와 observer가 느슨하게 묶임 (서로가 서로를 모름)
- subject가 observer에 대해 아는건 observer가 특정 인터페이스를 구현한다는 것 뿐 : update()
- observer는 언제든 새로 추가 가능
- 새 observer를 추가해도 subject를 변경할 필요 없음
- subject 와 observer는 독립적으로 재사용 가능
- subject 또는 observer가 변경되어도 서로 영향 안 미침
--> 변경사항이 생겨도, 객체사이의 상호 의존성을 최소화하여 변경가능한 유연성 확보 가능

기상 스테이션 설계
...
Java에서는...
java.util.Observer 인터페이스, java.util.Observeable 클래스 가 있음
스윙에서도 옵저버 패턴을 사용함 (e.g. JButton 의 ActionListener)
참고자료
닷넷환경 UML : http://www.zdnet.co.kr/builder/dev/modeling/0,39031637,39134439,00.htm
StarUML : http://cafe.naver.com/staruml
샤프심 흑심 품다 : http://blog.empas.com/mcm27xx/read.html?a=4093369
UML 이해하기 : Download



