アダプタパターンとデコレータパターンの違いと実装方法

adapter-decorator-pattern

今日のトピックは「アダプタパターンとデコレータパターン」です。アダプタパターンとデコレータパターンは、デザインパターンの中でも広く使われている2つのパターンです。アダプタパターンは、異なるインターフェースを持つクラス同士を接続するために使用され、デコレータパターンはオブジェクトに動的に機能を追加するために使用されます。これらのパターンを理解し、適切に使い分けることで、より柔軟で保守性の高いコードを実装することが可能になります。

目次

基本概念の説明

アダプタパターン (Adapter Pattern)

アダプタパターンは、既存のクラスを別のインターフェースに適合させるためのデザインパターンです。異なるインターフェースを持つクラスを統一的に扱えるようにすることで、既存のコードを変更せずに新しい機能を追加できます。

デコレータパターン (Decorator Pattern)

デコレータパターンは、オブジェクトに対して動的に新しい機能を追加するためのデザインパターンです。このパターンを使用することで、継承を使わずにオブジェクトの振る舞いを変更することができます。デコレータは、元のオブジェクトと同じインターフェースを実装し、元のオブジェクトのメソッドをラップします。

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

Python

アダプタパターン

class JapaneseSpeaker:
    def hello(self):
        return "こんにちは"

class EnglishSpeaker:
    def greet(self):
        return "Hello"

class EnglishAdapter:
    def __init__(self, english_speaker):
        self.english_speaker = english_speaker

    def hello(self):
        return self.english_speaker.greet()

# 使用例
japanese = JapaneseSpeaker()
english = EnglishAdapter(EnglishSpeaker())

print(japanese.hello())  # こんにちは
print(english.hello())   # Hello

デコレータパターン

class Coffee:
    def cost(self):
        return 5

class MilkDecorator:
    def __init__(self, coffee):
        self.coffee = coffee

    def cost(self):
        return self.coffee.cost() + 2

class SugarDecorator:
    def __init__(self, coffee):
        self.coffee = coffee

    def cost(self):
        return self.coffee.cost() + 1

# 使用例
coffee = Coffee()
milk_coffee = MilkDecorator(coffee)
sugar_milk_coffee = SugarDecorator(milk_coffee)

print(sugar_milk_coffee.cost())  # 8

C#

アダプタパターン

public class JapaneseSpeaker {
    public string Hello() {
        return "こんにちは";
    }
}

public class EnglishSpeaker {
    public string Greet() {
        return "Hello";
    }
}

public class EnglishAdapter : JapaneseSpeaker {
    private EnglishSpeaker englishSpeaker;

    public EnglishAdapter(EnglishSpeaker speaker) {
        this.englishSpeaker = speaker;
    }

    public new string Hello() {
        return englishSpeaker.Greet();
    }
}

// 使用例
JapaneseSpeaker japanese = new JapaneseSpeaker();
EnglishAdapter english = new EnglishAdapter(new EnglishSpeaker());

Console.WriteLine(japanese.Hello());  // こんにちは
Console.WriteLine(english.Hello());   // Hello

デコレータパターン

public class Coffee {
    public virtual int Cost() {
        return 5;
    }
}

public class MilkDecorator : Coffee {
    private Coffee coffee;

    public MilkDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    public override int Cost() {
        return coffee.Cost() + 2;
    }
}

public class SugarDecorator : Coffee {
    private Coffee coffee;

    public SugarDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    public override int Cost() {
        return coffee.Cost() + 1;
    }
}

// 使用例
Coffee coffee = new Coffee();
Coffee milkCoffee = new MilkDecorator(coffee);
Coffee sugarMilkCoffee = new SugarDecorator(milkCoffee);

Console.WriteLine(sugarMilkCoffee.Cost());  // 8

C++

アダプタパターン

#include <iostream>
#include <string>

class JapaneseSpeaker {
public:
    std::string hello() {
        return "こんにちは";
    }
};

class EnglishSpeaker {
public:
    std::string greet() {
        return "Hello";
    }
};

class EnglishAdapter : public JapaneseSpeaker {
    EnglishSpeaker& englishSpeaker;
public:
    EnglishAdapter(EnglishSpeaker& speaker) : englishSpeaker(speaker) {}

    std::string hello() override {
        return englishSpeaker.greet();
    }
};

// 使用例
int main() {
    JapaneseSpeaker japanese;
    EnglishSpeaker english;
    EnglishAdapter adaptedEnglish(english);

    std::cout << japanese.hello() << std::endl;  // こんにちは
    std::cout << adaptedEnglish.hello() << std::endl;  // Hello

    return 0;
}

デコレータパターン

#include <iostream>

class Coffee {
public:
    virtual int cost() {
        return 5;
    }
};

class MilkDecorator : public Coffee {
    Coffee& coffee;
public:
    MilkDecorator(Coffee& c) : coffee(c) {}

    int cost() override {
        return coffee.cost() + 2;
    }
};

class SugarDecorator : public Coffee {
    Coffee& coffee;
public:
    SugarDecorator(Coffee& c) : coffee(c) {}

    int cost() override {
        return coffee.cost() + 1;
    }
};

// 使用例
int main() {
    Coffee coffee;
    MilkDecorator milkCoffee(coffee);
    SugarDecorator sugarMilkCoffee(milkCoffee);

    std::cout << sugarMilkCoffee.cost() << std::endl;  // 8

    return 0;
}

Java

アダプタパターン

class JapaneseSpeaker {
    public String hello() {
        return "こんにちは";
    }
}

class EnglishSpeaker {
    public String greet() {
        return "Hello";
    }
}

class EnglishAdapter extends JapaneseSpeaker {
    private EnglishSpeaker englishSpeaker;

    public EnglishAdapter(EnglishSpeaker speaker) {
        this.englishSpeaker = speaker;
    }

    @Override
    public String hello() {
        return englishSpeaker.greet();
    }
}

// 使用例
public class Main {
    public static void main(String[] args) {
        JapaneseSpeaker japanese = new JapaneseSpeaker();
        EnglishAdapter english = new EnglishAdapter(new EnglishSpeaker());

        System.out.println(japanese.hello());  // こんにちは
        System.out.println(english.hello());   // Hello
    }
}

デコレータパターン

class Coffee {
    public int cost() {
        return 5;
    }
}

class MilkDecorator extends Coffee {
    private Coffee coffee;

    public MilkDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public int cost() {
        return coffee.cost() + 2;
    }
}

class SugarDecorator extends Coffee {
    private Coffee coffee;

    public SugarDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public int cost() {
        return coffee.cost() + 1;
    }
}

// 使用例
public class Main {
    public static void main(String[] args) {
        Coffee coffee = new Coffee();
        Coffee milkCoffee = new MilkDecorator(coffee);
        Coffee sugarMilkCoffee = new SugarDecorator(milkCoffee);

        System.out.println(sugarMilkCoffee.cost());  // 8
    }
}

JavaScript

アダプタパターン

class JapaneseSpeaker {
    hello() {
        return "こんにちは";
    }
}

class EnglishSpeaker {
    greet() {
        return "Hello";
    }
}

class EnglishAdapter {
    constructor(englishSpeaker) {
        this.englishSpeaker = englishSpeaker;
    }

    hello() {
        return this.englishSpeaker.greet();
    }
}

// 使用例
const japanese = new JapaneseSpeaker();
const english = new EnglishAdapter(new EnglishSpeaker());

console.log(japanese.hello());  // こんにちは
console.log(english.hello());   // Hello

デコレータパターン

class Coffee {
    cost() {
        return 5;
    }
}

class MilkDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }

    cost() {
        return this.coffee.cost() + 2;
    }
}

class SugarDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }

    cost() {
        return this.coffee.cost() + 1;
    }
}

// 使用例
const coffee = new Coffee();
const milkCoffee = new MilkDecorator(coffee);
const sugarMilkCoffee = new SugarDecorator(milkCoffee);

console.log(sugarMilkCoffee.cost());  // 8

各言語の解説

言語アダプタパターンの実装方法デコレータパターンの実装方法特徴
Pythonインターフェース適合のためのクラスラップオブジェクトに動的に機能を追加するクラスラップシンプルで柔軟、直感的に使いやすい
C#インターフェースの継承とメソッドのオーバーライド継承を利用したクラスラップ強力な型安全性とエラーチェックが可能
C++継承とポインタを使用したクラスラップ仮想メソッドを使用してオブジェクトをラップする高速で効率的、特に低レベルプログラムで有効
Javaインターフェースの継承とメソッドのオーバーライド継承を利用したクラスラップ強力なオブジェクト指向機能と堅牢性
JavaScriptクラスやオブジェクトのラップオブジェクトやクラスに動的に機能を追加する関数柔軟でシンプル、フロントエンドやNode.js環境に最適

まとめ

アダプタパターンは、異なるインターフェースを持つクラスを統一的に扱うために使用され、既存のコードを変更せずに新しい機能を追加するのに役立ちます。一方、デコレータパターンは、オブジェクトに動的に機能を追加するのに適しており、継承を使わずに柔軟な機能拡張が可能です。次回は「ファクトリーパターンと抽象ファクトリーパターン」について学習しましょう。

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

コメント

コメントする

目次