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

【デザインパターン】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処理を実装する場合もこのパターンを適用する場合が多いです。

【デザインパターン】Chain of Responsibilityパターン

Chain of Responsibilityのサンプルコードです。

package org.example.chainofresponsibility;

public class Question {
    public int level;
    public Question(int level){
        this.level = level;
    }
}
package org.example.chainofresponsibility;

public abstract class Handler {
    private Handler next;

    public Handler setNext(Handler next)
    {
        this.next = next;
        return next;
    }

    public final void request(Question question)
    {
        if(judge(question)) {
            // 処理完了
        } else if(next != null) {
            next.request(question);
        } else {
            // 処理不可能
        }
    }

    protected abstract boolean judge(Question question);
}
package org.example.chainofresponsibility;

public class ConcreteHandler1 extends Handler {
    @Override
    protected boolean judge(Question question) {
        if(question.level <= 1) {
            return true;
        } else {
            return false;
        }
    }
}
package org.example.chainofresponsibility;

public class ConcreteHandler2 extends Handler{
    @Override
    protected boolean judge(Question question) {
        if(question.level <= 2) {
            return true;
        } else {
            return false;
        }
    }
}
package org.example.chainofresponsibility;

public class Main {
    public static void main(String[] args)
    {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();

        handler1.setNext(handler2);

        Question question = new Question(1);

        handler1.request(question);
    }
}

Chain of Responsibilityは処理を行う人(Handler)を数珠つなぎに配列しておき、リクエスト(Question)に対して先頭のHandlerから処理が可能かどうかを判定(judge)し、処理できない物であれば後ろのHandlerにまる投げする、という仕組みです。

なので、あらかじめHandlerを継承しているConcreteHandlerを作成して数珠つなぎを作っておく必要があります。

ConcreteHandlerにてjudge=trueならば、渡されたquestionを適切に処理し、judge=falseならば、そのquestionを後ろのConcreteHanderに渡します。

デザインパターン】Proxyパターン

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

package org.example.proxy;

public interface Subject {
    void request1();
    void request2();
}
package org.example.proxy;

public class RealSubject implements Subject {
    @Override
    public void request1() {
        // 時間のかかる処理
    }

    @Override
    public void request2() {
        // 時間のかかる処理
    }
}
package org.example.proxy;

public class Proxy implements Subject{
    private RealSubject real;

    @Override
    public synchronized void request1() {
        realize();
        real.request1();
    }

    @Override
    public synchronized void request2() {
        realize();
        real.request2();
    }

    private void realize() {
        if(real == null) {
            real = new RealSubject();
        }
    }
}
package org.example.proxy;

public class Main {
    public static void main(String[] args) {
        Subject subject = new Proxy();
        subject.request1();
        subject.request2();
    }
}

Proxyパターンは時間のかかる処理を実行する際、クラスを一つ噛ませて一部の処理を代行(Proxy)するパターンです。

上のサンプルコードではRealSubjectが時間のかかる処理を行い、それを実行する前にProxyが実行されます。

ProxyとReadSubjectはSubjectという共通のインターフェースを持っています。

この例では、「直接ReadSubjectを実行してもいいんじゃね?」って思いますが、

実際に利用されるパターンとしては、ReadSubjectでは時間のかかる処理のみを実装し、それ以外の処理をProxyで処理させる、というのが一般的のようです。

デザインパターン】Flyweightパターン

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

package org.example.flyweight;

public class Flyweight {
    public void method1() {

    }

    public void method2() {

    }
}
package org.example.flyweight;

import java.util.HashMap;
import java.util.Map;

public class FlyweightFactory {
    private Map<Integer, Flyweight> pool = new HashMap<>();
    private static FlyweightFactory factory = new FlyweightFactory();

    private FlyweightFactory() {

    }

    public static FlyweightFactory getFactory() {
        return factory;
    }

    public Flyweight getFlyweight(int key) {
        Flyweight result;
        boolean exist = pool.containsKey(key);
        if(exist == true) {
            result = pool.get(key);
        } else {
            result = new Flyweight();
            pool.put(key, result);
        }

        return result;
    }
}
package org.example.flyweight;

public class Main {
    public static void main(String[] args) {
        FlyweightFactory factory = FlyweightFactory.getFactory();
        Flyweight object = factory.getFlyweight(0);
        object.method1();
        object.method2();
    }
}

Flyweightパターンは一度作成したインスタンスを保持して再利用することによってインスタンス作成時の負荷を軽減させるパターンです。

サンプルコードではFlyweightのオブジェクトをFlyweightFactory内のpoolに保持し、一度使用したものはpoolから取得するようにしています。

FlyweightFactoryはSingletonパターンで使用するのが一般的です。

poolは、サンプルではHashMapを使用しましたが、Collectionのインターフェースを持つクラス(Listなど)でも使用できます。

このあたりは実際の設計に合わせて実装していくことになると思います。

デザインパターン】Facadeパターン

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

package org.example.facade;

public class Class1 {
    public void method1()
    {

    }
}
package org.example.facade;

public class Class2 {
    public void method2()
    {

    }
}
package org.example.facade;

public class Facade {
    Class1 class1;
    Class2 class2;

    public Facade()
    {
        class1 = new Class1();
        class2 = new Class2();
    }
    public void function()
    {
        class1.method1();
        class2.method2();
    }
}
package org.example.facade;

public class Main {
    public static void main(String[] args)
    {
        Facade facade = new Facade();
        facade.function();
    }
}

Facadeパターンは、様々な機能を持つクラスの処理を、Facadeクラスの窓口に一本化するパターンです。

よく利用する一連の処理をFacadeのメソッドとして定義することで、扱いが簡単になります。

もしかしたら、デザインパターンを意識しなくてもやっているかもしれない。

デザインパターン】Decoratorパターン

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

package org.example.decorator;

public abstract class Component {
    public abstract void method1();
    public abstract void method2();
}
package org.example.decorator;

public class ConcreteComponent extends Component {
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public abstract class Decorator extends Component {
    protected Component component;

    protected Decorator(Component component) {
        super();
        this.component = component;
    }
}
package org.example.decorator;

public class ConcreteDecorator extends Decorator {
    protected ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public class Main {
    public static void main(String[] args)
    {
        Component component = new ConcreteComponent();
        Component instance = new ConcreteDecorator(component);
        instance.method1();
        instance.method2();

        Component component1 = new ConcreteDecorator(new ConcreteDecorator(component));
        component1.method1();
        component1.method2();
    }
}

このパターンでは、中身(ConcreteComponent)と装飾(ConcreteDecorator)が存在し、Componentという共通のインターフェースを持っています。

機能を追加する場合はDecoreterを継承したクラスを作成し、機能を拡張することができます。

ConcreteDecoratorのコンストラクタにはConcreteComponentを渡すこともできますし、すでに作成したConcreteDecoratorを渡すこともでき、それによって再帰的な処理をすることもできます。

しかし、クラスの関係が複雑になったりするので、クラス図による整理が必要になりますね。

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

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

package org.example.composite;

public abstract class Component {
    public abstract void method1();
    public abstract void method2();
    protected void add(Component component){}
}
package org.example.composite;

public class Leaf extends Component{
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.composite;

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

public class Composite extends Component{
    private List<Component> ComponentList = new ArrayList<>();

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void add(Component component) {
        ComponentList.add(component);
    }
}
package org.example.composite;

public class Main {
    public static void main(String[] args) {
        Component parent1 = new Composite();
        Component parent2 = new Composite();
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();

        parent2.add(leaf1);
        parent2.add(leaf2);
        parent1.add(parent2);
    }
}

Compositeパターンは木構造を構成してオブジェクトを管理し、再帰的に使用したい場合に使用します。

Compositeクラスが木構造の親クラス、Leafクラスが木構造の末端のクラスです。どちらもComponentクラスを継承します。

Leafクラスでも使用するため、Componentクラスのaddメソッドはprotectedの処理無しで作成します。

実際に追加する処理はCompositeクラスのaddメソッドをオーバーライドして作成します。