「デザインパターン」カテゴリーアーカイブ

【デザインパターン】Visitorパターン

Visitorパターンはデータ部と処理部に階層を分けて処理を定義するパターンです。

package com.example.visitor;

public interface Visitor {
	public void visit(ConcreteElement1 group);
	public void visit(ConcreteElement2 department);
}
package com.example.visitor;

public class ConcreteVisitor implements Visitor {
	public void visit(ConcreteElement1 group) {
	}
	
	public void visit(ConcreteElement2 department) {
	}
}
package com.example.visitor;

public interface Element {
	public void accept(Visitor visitor);
}
package com.example.visitor;

public class ConcreteElement1 implements Element {
	public ConcreteElement1() {
	}
	
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
}
package com.example.visitor;

public class ConcreteElement2 implements Element{
	public ConcreteElement2() {
	}
	
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
}
package com.example.visitor;

public class Main {

	public static void main(String[] args) {
		try {
			Element element1 = new ConcreteElement1();
			Element element2 = new ConcreteElement2();
			
			element1.accept(new ConcreteVisitor());
			element2.accept(new ConcreteVisitor());
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

}

Elementがデータ部、Visitorが処理部になっていて、

visitを実行時にElementのクラスによって実行する処理を切り替えています。

【デザインパターン】Template Methodパターン

Template Methodパターンは、決まり切っている処理を繰り返し使用することができるパターンです。

package com.example.templateMethod;

public abstract class AbstractClass {

	protected abstract void before();

	protected abstract void process();
	
	protected abstract void after();

	public final void execule() {
		before();
		process();
		after();
	}
}
package com.example.templateMethod;

public class ConcreteClass extends AbstractClass {

	public ConcreteClass() {
		
	}
	
	@Override
	protected void before() {
		
	}

	@Override
	protected void process() {
		
	}

	@Override
	protected void after() {
		
	}

}
package com.example.templateMethod;

public class Main {

	public static void main(String[] args) {
		AbstractClass process = new ConcreteClass();
		process.execule();
	}
}

このケースではbefore()→process()→after()の順に処理する、というのが決まっています。

あとは、このAbstractClassを実装する形でクラスを作成していけばよい、ということになります。

【デザインパターン】Strategyパターン

Strategyパターンはある条件をきっかけに中の処理を切り替えることができるパターンです。

Contextの中のStrategyを置き換えることで動作が切り替わります。

package com.example.strategy;

public interface Strategy {
	public void methodA();
	public void methodB();
}
package com.example.strategy;

public class ConcreteStrategy1 implements Strategy {

	public void methodA() {
	}

	public void methodB() {
	}

}
package com.example.strategy;

public class ConcreteStrategy2 implements Strategy{

	public void methodA() {
	}

	public void methodB() {
	}
	
}
package com.example.strategy;

public class Context {
	private Strategy strategy;
	
	public Context(Strategy strategy) {
		this.strategy = strategy;
	}

	public void methodA() {
		strategy.methodA();
	}

	public void methodB() {
		strategy.methodB();
	}
}
package com.example.strategy;

public class Main {
	public static void main(String[] args) {
		Context context1 = new Context(new ConcreteStrategy1());
		Context context2 = new Context(new ConcreteStrategy2());

		context1.methodA();
		context1.methodB();
		
		context2.methodA();
		context2.methodB();
	}
}

【デザインパターン】stateパターン

stateパターンは状態を表すクラスを導入することによって、状態に応じた処理を実行させるパターンです。

通常はif文等で状態を表す変数を評価して判断しますが、これをクラスに置き換えて処理するものです。

package com.example.state;

import java.text.SimpleDateFormat;

public interface State {
	public final static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
	
	public void setState(Context context);
	public void methodA();
	public void methodB();
}
package com.example.state;

public class ConcreteState1 implements State {
	private final static State state = new ConcreteState1();

	private ConcreteState1() {
		super();
	}
	
	public static State getInstance() {
		return state;
	}
	
	public void setState(Context context) {
		int value = context.getValue();
		if(value == 1) {
			context.changeState(ConcreteState2.getInstance());
		}
	}

	public void methodA() {
		
	}

	public void methodB() {
		
	}

}
package com.example.state;

public class ConcreteState2 implements State{
	private final static State state = new ConcreteState2();
	
	private ConcreteState2() {
		super();
	}
	
	public static State getInstance() {
		return state;
	}
	
	public void setState(Context context) {
		int value = context.getValue();
		if(value == 2) {
			context.changeState(ConcreteState1.getInstance());
		}
	}

	public void methodA() {
	}

	public void methodB() {
	}
}
package com.example.state;

public class Context {
	private int value;
	private State state = ConcreteState2.getInstance();
	
	public int getValue() {
		return value;
	}
	
	public void setValue(int value) {
		this.value = value;
		state.setState(this);
	}
	
	public void changeState(State state) {
		this.state = state;
	}
	
	public void methodA() {
		state.methodA();
	}
	
	public void methodB() {
		state.methodB();
	}
}
package com.example.state;

public class Main {
	public static void main(String args[]) {
		Context context = new Context();
		
		context.setValue(1);
		context.methodA();
		context.methodB();
		context.setValue(2);
		context.methodA();
		context.methodB();
	}

}

設定した値に応じてcontextに保持するクラスのインスタンスを保持し、

その状態に応じて実行する処理を、クラスの実装で処理を変更させています。

【デザインパターン】Observerパターン

Observerとは監視者という意味で、何らかのイベントを監視して実行させるパターンです。

package com.example.observer;

public interface Observer {
public void update(Event event);
}
package com.example.observer;

public class ConcreteObserver implements Observer {
@Override
public void update(Event event) {

}
}
package com.example.observer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public abstract class Event {
private List<Observer> observerList = new ArrayList<>();

public void addObserver(Observer observer) {
observerList.add(observer);
}

public void deleteObserver(Observer observer) {
observerList.remove(observer);
}

public void notifyObserver() {
Iterator it = observerList.iterator();

while(it.hasNext()) {
Observer observer = (Observer)it.next();
observer.update(this);
}
}

public abstract void execute();
}
package com.example.observer;

public class ConcreteEvent extends Event {
@Override
public void execute() {
notifyObserver();
}
}
package com.example.observer;

public class Main {
public static void main(String[] args) {
Observer observer1 = new ConcreteObserver();
Observer observer2 = new ConcreteObserver();

Event event = new ConcreteEvent();
event.addObserver(observer1);
event.addObserver(observer2);

event.execute();
}
}

Observerを実装したクラスをインスタンス化してEventに追加し、後ほどEventのexecute()を実行することでupdate()の処理が実行されます。

まぁ、Observerはexecute()の処理実行を監視しているイメージです。

【デザインパターン】MEMENTOパターン

処理前の内部状態(Memento)を保持し、状態を復元する仕組みです。

package com.example.memento;

public class Memento {
    int protectedInfo;

    Memento(int protectedInfo) {
        this.protectedInfo = protectedInfo;
    }

    int getProtectedInfo() {
        return protectedInfo;
    }

    public int getPublicInfo() {
        return protectedInfo;
    }
}
package com.example.memento;

public class Originator {
    private int info;

    public Originator(int info) {
        this.info = info;
    }

    public Memento createMemento() {
        Memento memento = new Memento(info);
        return memento;
    }

    public void restoreMemento(Memento memento) {
        this.info = memento.protectedInfo;
    }

    public void process() {
        info++;
    }

    public String toString() {
        return Integer.toString(info);
    }
}
package com.example.main;

import com.example.memento.Memento;
import com.example.memento.Originator;

public class Caretaker {
public static void main(String[] args) {
int info = 1;

Originator originator = new Originator(info);
Memento memento = originator.createMemento();

originator.process(); // 更新
originator.toString(); // 出力
originator.restoreMemento(memento); // レストア
originator.toString(); // 出力
}
}

MementoとOriginator は同じパッケージに含まれますが、Caretaker は別のパッケージで構成されます。

Caretaker はMementoを保持し、好きなタイミングで復元することができますが、Caretaker は直接Mementoのデータを書き換えることはできません。

なので、データの一貫性を保持しつつ、データを復元することができるようになります。

【デザインパターン】Mediatorパターン

複数のオブジェクト(Colleague)の制御を行うのにMediatorクラスを作成し、これを介して制御を行います。

package org.example.mediator;

public interface Mediator {
    public void createColleagues();
    public void colleagueChanged();
}
package org.example.mediator;

public abstract class Colleague {
protected Mediator mediator;
public abstract void setMediator(Mediator mediator);
public abstract void controlColleague();
}
package org.example.mediator;

public class ConcreteColleague1 extends Colleague {
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}

@Override
public void controlColleague() {
}
}
package org.example.mediator;

public class ConcreteColleague2 extends Colleague {
@Override
public void setMediator(Mediator mediator)
{
this.mediator = mediator;
}

@Override
public void controlColleague()
{
}
}
package org.example.mediator;

public class ConcreteMediator implements Mediator{
private Colleague concreteColleague1;
private Colleague concreteColleague2;

@Override
public void createColleagues() {
concreteColleague1 = new ConcreteColleague1();
concreteColleague1.setMediator(this);
concreteColleague2 = new ConcreteColleague1();
concreteColleague2.setMediator(this);
}

@Override
public void colleagueChanged() {
concreteColleague1.controlColleague();
concreteColleague2.controlColleague();
}
}
package org.example.mediator;

import java.util.Scanner;

public class Main {
public static void main(String[] args)
{
Mediator mediator = new ConcreteMediator();
mediator.createColleagues();
mediator.colleagueChanged();
}
}

【デザインパターン】Iteratorパターン

同じようなオブジェクトを整列させて管理したいときに使用します。

イメージとしてはLISTに近い。

package org.example.iterator;

public interface Iterator {
    public Object next();
    public boolean hasNext();
}
package org.example.iterator;

public interface Aggregate {
    public Iterator iterator();
}
package org.example.iterator;

public class ConcreteAggregate implements Aggregate{
    private Object[] objects;
    private int last = 0;

    public ConcreteAggregate(int maxSize)
    {
        this.objects = new Object[maxSize];
    }

    public void add(Object object)
    {
        objects[last] = object;
        last++;
    }

    public Object getItem(int index)
    {
        return objects[index];
    }

    public int getLast()
    {
        return last;
    }

    @Override
    public Iterator iterator() {
        return new ConcreteIterator(this);
    }
}
package org.example.iterator;

public class ConcreteIterator implements Iterator {
    private ConcreteAggregate aggregate;
    private int index;

    public ConcreteIterator(ConcreteAggregate aggregate)
    {
        this.aggregate = aggregate;
        this.index = 0;
    }

    @Override
    public Object next() {
        Object object = aggregate.getItem(index);
        index++;
        return object;
    }

    @Override
    public boolean hasNext() {
        if(index < aggregate.getLast())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}
package org.example.iterator;

public class Item {
}
package org.example.iterator;

public class Main {
    public static void main(String[] args)
    {
        ConcreteAggregate aggregate = new ConcreteAggregate(10);
        aggregate.add(new Item());
        aggregate.add(new Item());
        aggregate.add(new Item());

        Iterator iterator = aggregate.iterator();
        while(iterator.hasNext())
        {
            Item item = (Item)iterator.next();
        }
    }
}

まぁ、こんなコード書かなくても、すでにIteratorクラスは実装されているので。

Listのオブジェクトからiterator()で取得することもできます。

【デザインパターン】Interpreterパターン

Interpreterパターンのサンプルコードをまとめようと思ったのですが・・・

うまくまとまりませんでした。

Interpreterパターンは言語解析の用途に使用されます。

クラス図にするとこんな感じです。

https://www.techscore.com/tech/DesignPattern/Interpreter.html/

package org.example.interpreter;

public abstract class AbstractExpression {
    public abstract void interpret(String context);
}
package org.example.interpreter;

import java.util.ArrayList;

public class NonTerminalExpression  extends  AbstractExpression{
    @Override
    public void interpret(String context) {
        String[] contexts = context.split(",");
        for(int i = 0; i < contexts.length; i++)
        {
            AbstractExpression child = new TerminalExpression();
            child.interpret(contexts[i]);
        }
    }
}
package org.example.interpreter;

public class TerminalExpression extends AbstractExpression{


    @Override
    public void interpret(String context) {

    }
}
package org.example.interpreter;

public class Main {
    public static void main(String args[])
    {
        AbstractExpression child = new NonTerminalExpression();
        child.interpret(args[0]);
    }
}

言語を文節に分けるのはNonTerminalExpression、実際に解析を行うのはTerminalExpressionで処理を行います。

NonTerminalExpressionでは必要に応じて次のNonTerminalExpressionやTerminalExpressionをインスタンス化して使用されます。

【デザインパターン】Commandパターン

Commandパターンのサンプルコードです。

package org.example.command;

public interface Command {
public void execute() throws Exception;
}
package org.example.command;

public class Receiver {
public void action()
{

}
}
package org.example.command;

public class ConcreteCommand implements Command{
@Override
public void execute() throws Exception {
Receiver receiver = new Receiver();
receiver.action();
}
}
package org.example.command;

public class Main {
public static void main(String[] args)
{
Command command = new ConcreteCommand();
try {
command.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}

CommandパターンはReceiverに対する命令(Command)をオブジェクト化して使用します。

こうすることによって、命令のバッチ処理、履歴保存ができます。

undo/redo処理を実装する場合もこのパターンを適用する場合が多いです。