template method 인 go 메서드는 하위 클래스에서 재정의 할 수 없도록 final 로 선언한다.
fly() 메서드는 {} 가 있기 때문에 abstract 메소드가 아니다.
그렇기 때문에 하위 클래스에서 재정의 할 수 있고, 이러한 메서드는 훅메서드라 한다.
훅 메서드는 하위 클래스에서 구현해도 되고 안해도 되는 경우에 사용된다.
publicclassBeginnerLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("천천히 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("Jump 할 줄 모르지롱.");
}
@Overridepublicvoidturn(){
System.out.println("Turn 할 줄 모르지롱.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 초보자 레벨 입니다. *****");
}
}
publicclassAdvancedLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("빨리 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("높이 jump 합니다.");
}
@Overridepublicvoidturn(){
System.out.println("Turn 할 줄 모르지롱.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 중급자 레벨 입니다. *****");
}
}
publicclassSuperLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("엄청 빨리 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("아주 높이 jump 합니다.");
}
@Overridepublicvoidturn(){
System.out.println("한 바퀴 돕니다.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 고급자 레벨 입니다. *****");
}
}
publicabstractclassNumberGenerator {
privateList<Observer> observers = new ArrayList<Observer>(); // Observer들을 보관publicvoidaddObserver(Observer observer) { // Observer를 추가
observers.add(observer);
}
publicvoiddeleteObserver(Observer observer) { // Observer를 삭제
observers.remove(observer);
}
publicvoidnotifyObservers() { // Observer에 통지
Iterator<Observer> it = observers.iterator();
while (it.hasNext()) {
Observer o = it.next();
o.update(this);
}
}
publicabstractintgetNumber(); // 수를 취득한다.publicabstractvoidexecute(); // 수를 생성한다.
}
class diagram 에서 Subject에 해당된다.
위와 같이, Subject와 Observer간의 추상적인 결합만이 존재한다.
위와 같이 Observer 를 리스트 형태로 가지고 있는 것을 확인할 수 있다.
notifyObservers 메서드에서 Observer 를 순회하면서 update 를 친다.
publicclassRandomNumberGeneratorextendsNumberGenerator{
private Random random = new Random(); // 난수발생기private int number; // 현재의 수public int getNumber() { // 수를 취득한다.returnnumber;
}
publicvoidexecute() {
for (int i = 0; i < 20; i++) {
number = random.nextInt(50);
notifyObservers();
}
}
}
class diagram 에서 ConcreteSubject에 해당된다.
execute 메서드를 호출해서 notifyObservers 를 호출하고 notifyObservers 메서드에서 Observer 를 순회하며 update 를 친다.
NumberGenerator(Subject) 를 매개변수로 가지고 있는 것을 확인할 수 있다.
publicclassGraphObserverimplementsObserver{
publicvoid update(NumberGenerator generator) {
System.out.print("GraphObserver:");
int count = generator.getNumber();
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
class diagram 에서 ConcreteObserver에 해당된다.
publicclassMain{
publicstaticvoid main(String[] args) {
NumberGenerator generator = new RandomNumberGenerator();
Observer observer1 = new DigitObserver();
Observer observer2 = new GraphObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
generator.execute();
}
}
Chain of Responsibility Pattern
어떠한 객체의 책임만으로 해결하기 힘든 문제를 책임을 떠넘겨서 해결할 수 있을 경우 사용하면 좋은 패턴이다.
다수의 객체를 사슬처럼 연결한다.
요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여한다.
요청을 해결할 객체를 만날 때까지 객체 고리를 따라서 요청을 전달한다.
메세지를 보내는 객체와 이를 받아서 처리하는 객체들 간의 결합도를 줄일 수 있다.
연결순서는 상황에 따라 바뀌거나 추가 삭제될 수 있다. 즉 객체의 책임을 추가, 변경, 확장할 수 있다.
하지만, 메세지가 항상 수신된다는것을 보장할 수 없다.
Class diagram
Handler
요청을 처리하는 인터페이스를 정의하고, 다음 번 처리자와의 연결을 구현한다.
연결고리에 연결된 다음 객체에게 다시 메세지를 보낸다.
ConcreteHandler
책임져야 할 메세지를 처리한다.
처리못하는 메세지는 다음 수신자에게 전달한다.
Client
ConcreteHandler 객체에게 필요한 요청을 보낸다.
예제 코드
publicabstractclassSupport{
privateString name; // 트러블 해결자의 이름private Support next; // 떠넘기는 곳publicSupport(String name) { // 트러블 해결자의 생성this.name = name;
}
public Support setNext(Support next) { // 떠넘길 곳을 설정this.next = next;
return next;
}
public final voidsupport(Trouble trouble) { // 트러블 해결 순서if (resolve(trouble)) {
done(trouble);
} elseif (next != null) {
next.support(trouble);
} else {
fail(trouble);
}
}
publicStringtoString() { // 문자열 표현return"[" + name + "]";
}
protectedabstractboolean resolve(Trouble trouble); // 해결용 메소드protectedvoiddone(Trouble trouble) { // 해결
System.out.println(trouble + " is resolved by " + this + ".");
}
protectedvoidfail(Trouble trouble) { // 미해결
System.out.println(trouble + " cannot be resolved.");
}
}
class diagram 에서 Handler 에 해당된다.
Chain 을 구현하기 위해서 setNext 의 반환값을 Support 자기자신으로 설정한다.
support 는 오버라이드 할 수 없도록 final 로 구현했다. (Template Method Pattern)
support 메서드는 문제를 해결하면 종료하고 해결하지 못하면 next 의 support 를 호출한다.
next 가 없을 때까지 진행했음에도 해결하지 못하면 실패한다.
public classLimitSupportextendsSupport{
private int limit; // 이 번호 미만이면 해결 할수 있다.
public LimitSupport(String name, int limit) { // 생성자super(name);
this.limit = limit;
}
protected boolean resolve(Trouble trouble) { // 해결용 메소드if (trouble.getNumber() < limit) {
returntrue;
} else {
returnfalse;
}
}
}
class diagram 에서 ConcreteHandler에 해당된다.
public classSpecialSupportextendsSupport{
private int number; // 이 번호만 해결할 수 있다.
public SpecialSupport(String name, int number) { // 생성자super(name);
this.number = number;
}
protected boolean resolve(Trouble trouble) { // 해결용 메소드 if (trouble.getNumber() == number) {
returntrue;
} else {
returnfalse;
}
}
}
public classNoSupportextendsSupport{
public NoSupport(String name) {
super(name);
}
protected boolean resolve(Trouble trouble) { // 해결용 메소드returnfalse; // 자신은 아무 처리도 하지 않는다.
}
}
publicclassMain{
publicstaticvoidmain(String[] args){
Support alice = new NoSupport("Alice");
Support bob = new LimitSupport("Bob", 100);
Support charlie = new SpecialSupport("Charlie", 429);
Support diana = new LimitSupport("Diana", 200);
Support elmo = new OddSupport("Elmo");
Support fred = new LimitSupport("Fred", 300);
// 연쇄의 형성
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
// 다양한 트러블 발생for (int i = 0; i < 500; i += 33) {
alice.support(new Trouble(i));
}
}
}
publicclassMain {
publicstaticvoidmain(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator it = bookShelf.iterator(Constant.FORWARD);
while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println("" + book.getName());
}
System.out.println("============");
it = bookShelf.iterator(Constant.REVERSE);
while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println("" + book.getName());
}
}
}
위의 결과는 FORWARD 일때와 REVERSE 일때가 반대로 동작하는 것을 확인할 수 있다.
Visitor Pattern
SOILD 원칙에서는 클래스가 해야될 일은 해당 클래스 내에서 해야된다고 나온다.
하지만 Visitor Pattern 은 클래스 밖에서 일을 처리한다.
element에서 처리할 일을 visitor 가 처리한다.
여러 element에 대해 유사한 기능의 메서드를 한곳에 모아서 관리하게 되고, 여러 클래스에 걸친 메서드를 추가하기 용이하다.
각 클래스에 대한 기능이 자주 변경되거나 알고리즘의 변화가 많을때 사용하는것이 효율적이다.
각 객체의 오퍼레이션이 외부에 노출되는 것이므로 객체지향 프로그래밍의 캡슐화 전략에 위배된다.
주로 Composite 패턴에서 자주 사용될 수 있다.
class diagram
Visitor
각 객체에서 수행해야 할 기능을 선언한다.
메서드의 이름은 요청을 받을 객체를 명시한다.
ConcreteVisitor
Visitor 클래스에 선언된 메서드를 구현한다.
각 메서드는 객체에 해당하는 알고리즘을 구현한다.
Element
Visitor가 수행될 수 있는 Accept() 메서드를 선언한다.
ConcreteElement
매개변수로 Visitor를 받아주는 Accept()메서드를 구현한다.
ConcreteAggregate
해당하는 ConcreteIteratir의 인스턴스를 반환하도록 Iterator 생성 인터페이스를 구현한다.
ConcreteElement 들이 하는 일을 ConcreteVisitor 가 방문하면서 대신 처리한다.
Visitor 가 추가될 때 사용하면 좋은 패턴이나, Element 가 추가되는 경우에는 사용하면 좋지 않은 패턴이다.
새로운 요소 클래스를 추가하게 되면 그에 해당되는 기능을 모든 Visitor에서 구현해야 하기 때문이다.
Acceptor 의 accept 메소드를 구현하고 있지 않기 때문에 abstract 클래스로 구현한다.
publicclassFileextendsEntry{
privateString name;
private int size;
publicFile(String name, int size) {
this.name = name;
this.size = size;
}
publicStringgetName() {
return name;
}
public int getSize() {
return size;
}
publicvoidaccept(Visitor v) {
v.visit(this);
}
}
class diagram 에서 ConcreteElement 에 해당된다.
accept 메서드에서 visit 메서드를 호출하면서 인자로 this 를 넘긴다.
ConcreteElement 가 추가되는 경우 사용하면 좋지 않은 패턴이다.
publicclassDirectoryextendsEntry{
privateString name; // 디렉토리의 이름private ArrayList<Entry> dir = new ArrayList<Entry>(); // 디렉토리 엔트리의 집합publicDirectory(String name) { // 생성자this.name = name;
}
publicStringgetName() { // 이름을 얻는다.return name;
}
public int getSize() { // 사이즈를 얻는다.
int size = 0;
Iterator<Entry> it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) { // 엔트리의 추가
dir.add(entry);
returnthis;
}
public Iterator<Entry> iterator() { // Iterator의 생성return dir.iterator();
}
publicvoidaccept(Visitor v) { // 방문자를 받아들임
v.visit(this);
}
}
publicclassMain {
publicstaticvoidmain(String[] args) {
try {
System.out.println("Making root entries...");
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.accept(new ListVisitor());
System.out.println("");
System.out.println("Making user entries...");
Directory Kim = new Directory("Kim");
Directory Lee = new Directory("Lee");
Directory Kang = new Directory("Kang");
usrdir.add(Kim);
usrdir.add(Lee);
usrdir.add(Kang);
Kim.add(new File("diary.html", 100));
Kim.add(new File("Composite.java", 200));
Lee.add(new File("memo.tex", 300));
Kang.add(new File("game.doc", 400));
Kang.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}
다음과 같이, new ListVisitor() 를 인자로 accept 를 호출하게 되면 ListVisitor 가 요소를 방문하면서 일을 처리한다.
ListVisitor 의 visit 을 호출할때 요소(this) 를 넘기기 때문이다.
public classFileTreatmentExceptionextendsRuntimeException{
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}
Command Pattern
동일한 코맨드로 만들면 모을수 있어서 히스토리 관리를 할 수 있다. 객체 Array에 담을수 있기때문, 롤백도 할 수 있다.
publicclassBeginnerLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("천천히 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("Jump 할 줄 모르지롱.");
}
@Overridepublicvoidturn(){
System.out.println("Turn 할 줄 모르지롱.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 초보자 레벨 입니다. *****");
}
}
class diagram 에서 ConcreteState에 해당한다.
publicclassAdvancedLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("빨리 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("높이 jump 합니다.");
}
@Overridepublicvoidturn(){
System.out.println("Turn 할 줄 모르지롱.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 중급자 레벨 입니다. *****");
}
}
class diagram 에서 ConcreteState에 해당한다.
publicclassSuperLevelextendsPlayerLevel{
@Overridepublicvoidrun(){
System.out.println("엄청 빨리 달립니다.");
}
@Overridepublicvoidjump(){
System.out.println("아주 높이 jump 합니다.");
}
@Overridepublicvoidturn(){
System.out.println("한 바퀴 돕니다.");
}
@OverridepublicvoidshowLevelMessage(){
System.out.println("***** 고급자 레벨 입니다. *****");
}
}