Help us understand the problem. What is going on with this article?

C#で学ぶデザインパターン入門 ②Adapter

More than 3 years have passed since last update.

概要

@hyuki 先生著の『Javaで学ぶデザインパターン入門』(2004年、SB Creative)の1章ずつをベースに、サンプルコードをC#で置き換えながら勉強していく記事です。

※著者の @hyuki 先生には適切に書籍への参照を入れれば問題ない旨ご確認いただいています。

本題

Adapterパターン
第2回はAdapterパターンです。Adapterパターンは本の表現を借りると、「すでに提供されているものと必要なものの間のずれを埋める」ようなデザインパターンです。
Adapterパターンには①Class Adapterパターン(継承を使う)②Object Adapterパターン(委譲を使う)の二種類があります。

サンプルコード

早速具体的な事例を見てみましょう。『Javaで学ぶデザインパターン入門』(2004年、SB Creative)に掲載されているコードをC#で(大体)書き換えます。

まずはClass Adapterパターンからです。

①Class Adapterパターン

// コンソールアプリケーションで実行を確認しました
using System;

namespace ClassAdapterPattern
{
    // Client
    class Program
    {
        static void Main(string[] args)
        {
            Print p = new PrintBanner("Hello");
            p.PrintWeak();
            // => (Hello)
            p.PrintStrong();
            // => *Hello*

            // 実行が一瞬で終わって確認できないので、キーの入力を待ちます
            Console.ReadLine();
        }
    }

    // このクラスは既に提供されているものとします
    // Adaptee
    public class Banner
    {
        private string str;
        public Banner(string str)
        {
            this.str = str;
        }
        public void ShowWithPattern()
        {
            Console.WriteLine($"({str})");
        }
        public void ShowWithAster()
        {
            Console.WriteLine($"*{str}*");
        }
    }

    // Target
    public interface Print
    {
        void PrintWeak();
        void PrintStrong();
    }

    // Adapter
    public class PrintBanner : Banner, Print
    {
        public PrintBanner(string str) : base(str) { }
        public void PrintWeak()
        {
            this.ShowWithPattern();
        }
        public void PrintStrong()
        {
            this.ShowWithAster();
        }
    }

}

次にObject Adapterパターンです。

②Object Adapterパターン

// コンソールアプリケーションで実行を確認しました
using System;

namespace ObjectAdapterPattern
{
    class Program
    {
        // Client
        static void Main(string[] args)
        {
            Print p = new PrintBanner("Hello");
            p.PrintWeak();
            // => (Hello)
            p.PrintStrong();
            // => *Hello*

            // 実行が一瞬で終わって確認できないので、キーの入力を待ちます
            Console.ReadLine();
        }
    }

    // このクラスは既に提供されているものとします
    // Adaptee
    public class Banner
    {
        private string str;
        public Banner(string str)
        {
            this.str = str;
        }
        public void ShowWithPattern()
        {
            Console.WriteLine($"({str})");
        }
        public void ShowWithAster()
        {
            Console.WriteLine($"*{str}*");
        }
    }

    // Target
    public abstract class Print
    {
        public abstract void PrintWeak();
        public abstract void PrintStrong();
    }

    // Adapter
    public class PrintBanner : Print
    {
        private Banner banner;
        public PrintBanner(string str)
        {
            this.banner = new Banner(str);
        }
        public override void PrintWeak()
        {
            this.banner.ShowWithPattern();
        }
        public override void PrintStrong()
        {
            this.banner.ShowWithAster();
        }
    }
}

効能

  • 既存のよくテストされて枯れたモジュールを使いまわせる。
  • 必要とするメソッド群を素早く準備できる。
  • バグが出た場合の修正箇所をadapter部分に絞り込める。

使用上の注意

  • clientから使用するときは必要な分だけ定義したIFを使う
  • サンプルだとPrint p = new PrintBanner("Hello");であって、PrintBanner p = new PrintBanner("Hello");ではない

①Class Adapterパターンと②Object Adapterパターンのどちらを使うのか?について『C#実践開発手法』に記述がありました。

Class Adapterパターンは、それほど使用されないAdapterパターンです。これは主に、開発者が継承よりも合成を優:先するように教えられているためです。継承はホワイトボックスの再利用であり、サブクラスをそのインターフェイスだけでなくクラスの実装にも依存させます。合成はブラックボックスの再利用であり、依存関係はインタイフェースに限定されるため、クライアントに悪影響をおよぼさずに実装を変更することが可能です。

まとめると、

  • 意図しない挙動を避けるため、モジュールの結合度を下げるためにClass AdapterパターンよりもObject Adapterパターンを使用した方がよいケースが多そう

ということでしょうか。

関連しているパターン

感想や疑問

  • 既存のメソッドを容易に変更し難い状況(internalなAPIでいろんなプラットフォームのクライアントから使ってて影響範囲がよく見えない、publicなAPI)だと確かにほしくなるのかなぁ。
  • どっちがましかは状況に依るのかなぁ。
    • APIのオプションでメソッド内がififしてしまう
    • APIをいじらないようにする前提でclientが増えてAdapterが乱立する

C#で学ぶデザインパターン入門

①Iterator
②Adapter
③Template Method
④Factory Method
⑤Singleton
⑥Prototype
⑦Builder
⑧AbstractFactory
⑨Bridge
⑩Strategy
⑪Composite Pattern
⑫Decorator Pattern

toshi0607
最近はGoとServerlessが好きです!
https://toshi0607.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした