ストラテジーパターンとステートパターンの違いと実装方法

strategy-state-pattern

今日のトピックは「ストラテジーパターンとステートパターン」です。ストラテジーパターンとステートパターンは、いずれもオブジェクトの振る舞いを動的に変更するためのデザインパターンですが、それぞれ異なる目的とアプローチを持ちます。ストラテジーパターンは、アルゴリズムをカプセル化し、実行時に選択できるようにするために使用され、ステートパターンはオブジェクトの内部状態に応じてその振る舞いを変更するために使用されます。

目次

基本概念の説明

ストラテジーパターン (Strategy Pattern)

ストラテジーパターンは、異なるアルゴリズムを定義し、それらをクライアントから切り離してカプセル化するためのデザインパターンです。これにより、アルゴリズムの変更が容易になり、実行時に動的にアルゴリズムを選択することが可能になります。

ステートパターン (State Pattern)

ステートパターンは、オブジェクトの内部状態に基づいて、その振る舞いを動的に変更するためのデザインパターンです。このパターンを使用することで、状態ごとの複雑な条件分岐を避け、コードをシンプルで管理しやすいものにできます。

各言語でのサンプルコード

Python

ストラテジーパターン

from abc import ABC, abstractmethod

class Strategy(ABC):
    @abstractmethod
    def execute(self, a, b):
        pass

class AddStrategy(Strategy):
    def execute(self, a, b):
        return a + b

class SubtractStrategy(Strategy):
    def execute(self, a, b):
        return a - b

class Context:
    def __init__(self, strategy: Strategy):
        self._strategy = strategy

    def set_strategy(self, strategy: Strategy):
        self._strategy = strategy

    def execute_strategy(self, a, b):
        return self._strategy.execute(a, b)

# 使用例
context = Context(AddStrategy())
print(context.execute_strategy(5, 3))  # 8

context.set_strategy(SubtractStrategy())
print(context.execute_strategy(5, 3))  # 2

ステートパターン

from abc import ABC, abstractmethod

class State(ABC):
    @abstractmethod
    def handle(self, context):
        pass

class ConcreteStateA(State):
    def handle(self, context):
        print("State A: Handling request")
        context.set_state(ConcreteStateB())

class ConcreteStateB(State):
    def handle(self, context):
        print("State B: Handling request")
        context.set_state(ConcreteStateA())

class Context:
    def __init__(self, state: State):
        self._state = state

    def set_state(self, state: State):
        self._state = state

    def request(self):
        self._state.handle(self)

# 使用例
context = Context(ConcreteStateA())
context.request()  # State A: Handling request
context.request()  # State B: Handling request

C#

ストラテジーパターン

using System;

public interface IStrategy {
    int Execute(int a, int b);
}

public class AddStrategy : IStrategy {
    public int Execute(int a, int b) {
        return a + b;
    }
}

public class SubtractStrategy : IStrategy {
    public int Execute(int a, int b) {
        return a - b;
    }
}

public class Context {
    private IStrategy _strategy;

    public Context(IStrategy strategy) {
        _strategy = strategy;
    }

    public void SetStrategy(IStrategy strategy) {
        _strategy = strategy;
    }

    public int ExecuteStrategy(int a, int b) {
        return _strategy.Execute(a, b);
    }
}

// 使用例
class Program {
    static void Main() {
        Context context = new Context(new AddStrategy());
        Console.WriteLine(context.ExecuteStrategy(5, 3));  // 8

        context.SetStrategy(new SubtractStrategy());
        Console.WriteLine(context.ExecuteStrategy(5, 3));  // 2
    }
}

ステートパターン

using System;

public interface IState {
    void Handle(Context context);
}

public class ConcreteStateA : IState {
    public void Handle(Context context) {
        Console.WriteLine("State A: Handling request");
        context.SetState(new ConcreteStateB());
    }
}

public class ConcreteStateB : IState {
    public void Handle(Context context) {
        Console.WriteLine("State B: Handling request");
        context.SetState(new ConcreteStateA());
    }
}

public class Context {
    private IState _state;

    public Context(IState state) {
        _state = state;
    }

    public void SetState(IState state) {
        _state = state;
    }

    public void Request() {
        _state.Handle(this);
    }
}

// 使用例
class Program {
    static void Main() {
        Context context = new Context(new ConcreteStateA());
        context.Request();  // State A: Handling request
        context.Request();  // State B: Handling request
    }
}

C++

ストラテジーパターン

#include <iostream>

class Strategy {
public:
    virtual int execute(int a, int b) const = 0;
    virtual ~Strategy() {}
};

class AddStrategy : public Strategy {
public:
    int execute(int a, int b) const override {
        return a + b;
    }
};

class SubtractStrategy : public Strategy {
public:
    int execute(int a, int b) const override {
        return a - b;
    }
};

class Context {
    const Strategy* strategy;
public:
    Context(const Strategy* strategy) : strategy(strategy) {}

    void set_strategy(const Strategy* new_strategy) {
        strategy = new_strategy;
    }

    int execute_strategy(int a, int b) const {
        return strategy->execute(a, b);
    }
};

// 使用例
int main() {
    AddStrategy add;
    SubtractStrategy subtract;
    Context context(&add);

    std::cout << context.execute_strategy(5, 3) << std::endl;  // 8
    context.set_strategy(&subtract);
    std::cout << context.execute_strategy(5, 3) << std::endl;  // 2

    return 0;
}

ステートパターン

#include <iostream>

class Context;

class State {
public:
    virtual void handle(Context* context) = 0;
    virtual ~State() {}
};

class ConcreteStateA : public State {
public:
    void handle(Context* context) override;
};

class ConcreteStateB : public State {
public:
    void handle(Context* context) override;
};

class Context {
    State* state;
public:
    Context(State* state) : state(state) {}

    void set_state(State* new_state) {
        state = new_state;
    }

    void request() {
        state->handle(this);
    }
};

// 実装
void ConcreteStateA::handle(Context* context) {
    std::cout << "State A: Handling request" << std::endl;
    context->set_state(new ConcreteStateB());
}

void ConcreteStateB::handle(Context* context) {
    std::cout << "State B: Handling request" << std::endl;
    context->set_state(new ConcreteStateA());
}

// 使用例
int main() {
    Context context(new ConcreteStateA());
    context.request();  // State A: Handling request
    context.request();  // State B: Handling request

    return 0;
}

Java

ストラテジーパターン

interface Strategy {
    int execute(int a, int b);
}

class AddStrategy implements Strategy {
    public int execute(int a, int b) {
        return a + b;
    }
}

class SubtractStrategy implements Strategy {
    public int execute(int a, int b) {
        return a - b;
    }
}

class Context {
    private Strategy strategy;

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

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}

// 使用例
public class Main {
    public static void main(String[] args) {
        Context context = new Context(new AddStrategy());
        System.out.println(context.executeStrategy(5, 3));  // 8

        context.setStrategy(new SubtractStrategy());
        System.out.println(context.executeStrategy(5, 3));  // 2
    }
}

ステートパターン

interface State {
    void handle(Context context);
}

class ConcreteStateA implements State {
    public void handle(Context context) {
        System.out.println("State A: Handling request");
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB implements State {
    public void handle(Context context) {
        System.out.println("State B: Handling request");
        context.setState(new ConcreteStateA());
    }
}

class Context {
    private State state;

    public Context(State state) {
        this.state = state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void request() {
        state.handle(this);
    }
}

// 使用例
public class Main {
    public static void main(String[] args) {
        Context context = new Context(new ConcreteStateA());
        context.request();  // State A: Handling request
        context.request();  // State B: Handling request
    }
}

JavaScript

ストラテジーパターン

class Strategy {
    execute(a, b) {
        throw new Error("This method should be overridden!");
    }
}

class AddStrategy extends Strategy {
    execute(a, b) {
        return a + b;
    }
}

class SubtractStrategy extends Strategy {
    execute(a, b) {
        return a - b;
    }
}

class Context {
    constructor(strategy) {
        this.strategy = strategy;
    }

    setStrategy(strategy) {
        this.strategy = strategy;
    }

    executeStrategy(a, b) {
        return this.strategy.execute(a, b);
    }
}

// 使用例
const context = new Context(new AddStrategy());
console.log(context.executeStrategy(5, 3));  // 8

context.setStrategy(new SubtractStrategy());
console.log(context.executeStrategy(5, 3));  // 2

ステートパターン

class State {
    handle(context) {
        throw new Error("This method should be overridden!");
    }
}

class ConcreteStateA extends State {
    handle(context) {
        console.log("State A: Handling request");
        context.setState(new ConcreteStateB());
    }
}

class ConcreteStateB extends State {
    handle(context) {
        console.log("State B: Handling request");
        context.setState(new ConcreteStateA());
    }
}

class Context {
    constructor(state) {
        this.state = state;
    }

    setState(state) {
        this.state = state;
    }

    request() {
        this.state.handle(this);
    }
}

// 使用例
const context = new Context(new ConcreteStateA());
context.request();  // State A: Handling request
context.request();  // State B: Handling request

各言語の解説

言語ストラテジーパターンの実装方法ステートパターンの実装方法特徴
Python抽象クラスと継承を使用してアルゴリズムをカプセル化状態クラスを使用してオブジェクトの振る舞いを制御シンプルで柔軟、直感的に実装可能
C#インターフェースとクラスを使用してアルゴリズムをカプセル化インターフェースとクラスを使用してオブジェクトの状態を管理強力な型安全性と拡張性、オブジェクト指向の設計が容易
C++仮想メソッドと継承を使用してアルゴリズムをカプセル化状態クラスを使用してオブジェクトの振る舞いを制御高速で効率的、特に低レベルプログラムで有効
Javaインターフェースとクラスを使用してアルゴリズムをカプセル化インターフェースとクラスを使用してオブジェクトの状態を管理強力なオブジェクト指向機能と堅牢性、Javaの標準APIと統合が容易
JavaScriptクラスと継承を使用してアルゴリズムをカプセル化状態クラスを使用してオブジェクトの振る舞いを制御柔軟でシンプル、フロントエンドやNode.js環境に最適

まとめ

ストラテジーパターンは、異なるアルゴリズムをカプセル化し、動的に切り替えることができるため、コードの再利用性と柔軟性を向上させます。一方、ステートパターンは、オブジェクトの内部状態に基づいて振る舞いを変更するため、複雑な条件分岐を避け、コードを簡潔に保つことができます。次回は「テンプレートメソッドパターンとファクトリメソッドパターン」について学習しましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次