1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C++】Abstract Factoryパターンでまとめてオブジェクト生成を!!

1
Posted at

開発をしていると、「関連するオブジェクトをまとめて生成したい」という場面が必ず出てきます。
例えば

  • UIのテーマが変わると、ボタン・テキスト・ウィンドウなど複数の部品を一括で切り替えたい
  • プラットフォームが異なると、対応する API 群をセットで生成したい

このように、複数のオブジェクトがセットで切り替わる状況では、個々のオブジェクトを直接newしていると、変更のたびにコードのあちこちを修正する必要があり、密結合で保守性の低い設計になってしまいます。

前回紹介したFactory Methodパターンでは、複数の関連オブジェクトをまとめて切り替えるといった要求には対応しきれません。
そこで登場するのがAbstract Factoryパターンです。
Abstract Factoryパターンを使用することで、関連するオブジェクト群を、統一されたインターフェースでまとめて生成することが可能になります。
本記事では、アプリケーション開発のUI描画を模したコードを使って Abstract Factoryパターンについてなるべくわかりやすく解説します。

前提知識

  • C++の基礎文法を理解している

Abstract Factoryパターンとは

Abstract Factoryパターンは、関連する複数のオブジェクトを、統一されたインターフェースを通してまとめて生成するためのデザインパターンです。

  • 関連するオブジェクト群をセットとして統一的に生成できる
  • 利用側は具象クラスを意識せずに済み、依存関係を減らせる
  • オブジェクト群の整合性を保ちやすい
    といったメリットがあります。
    その反面で、
  • クラス数が多くなりやすい
  • 構造が複雑になりやすく、小規模な用途では過剰設計になりがち
  • 新しいオブジェクト群を追加する場合、抽象インターフェースの変更が必要になる
    といったデメリットも存在します。

Abstract Factoryパターンの用語と構成

Abstract Factoryパターンは以下の要素で構成されます。

  • AbstractProduct/抽象製品
    • 生成物(Product)の共通インターフェース定義
    • ConcreteProductはこれを継承することで、差し替えの際に整合性が保てる
  • ConcreteProduct/具体製品
    • AbstractProductを実装したクラス
    • Factoryから実際に生成されるもの
  • AbstractFactory/抽象ファクトリ
    • Product群をまとめて生成するためのインターフェース定義
    • どのProductを生成するかは派生クラス(ConcreteFactory)が決める
  • ConcreteFactory/具体ファクトリ
    • AbstractFactoryを実装したクラス
    • 特定のProduct群を生成する
    • テーマ・プラットフォーム・設定などに応じて複数用意される

構造の図解

サンプルコード

テーマごとに異なるUIのセットを生成するコード

Button.h
	//-------------------------------------------------------------
	//! @file   Button.h
	//! @brief  抽象的なボタンのインターフェース定義
	//! @author つきの
	//-------------------------------------------------------------
#pragma once
//-------------------------------------------------------------
//! @class  Button
//! @brief  抽象的なボタンのインターフェース
//! @note   Abstract FactoryパターンのAbstractProductの役割を担う
//-------------------------------------------------------------
class Button {
public:
	//-------------------------------------------------------------
	//! @brief  ボタンを描画する純粋仮想関数
	//! @note   派生クラスで実装をする
	//-------------------------------------------------------------
	virtual void draw() = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~Button() = default;
};
Text.h
//-------------------------------------------------------------
//! @file   Text.h
//! @brief  抽象的なテキストのインターフェース定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
//-------------------------------------------------------------
//! @class  Text
//! @brief  抽象的なテキストのインターフェース
//! @note   Abstract FactoryパターンのAbstractProductの役割を担う
//-------------------------------------------------------------
class Text {
public:
	//-------------------------------------------------------------
	//! @brief  テキストを描画する純粋仮想関数
	//! @note   派生クラスで実装をする
	//-------------------------------------------------------------
	virtual void draw() = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~Text() = default;
};
DarkButton.h
//-------------------------------------------------------------
//! @file   DarkButton.h
//! @brief  Darkテーマ用のボタンの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Button.h"
//-------------------------------------------------------------
//! @class  DarkButton
//! @brief  Darkテーマ用のボタン
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class DarkButton : public Button {
public:
	//-------------------------------------------------------------
	// ボタンを描画する
	//-------------------------------------------------------------
	void draw()override;
};
DarkButton.cpp
//-------------------------------------------------------------
//! @file   DarkButton.cpp
//! @brief  Darkテーマ用のボタンの実装
//! @author つきの
//-------------------------------------------------------------
#include "DarkButton.h"
#include <iostream>
//-------------------------------------------------------------
//! @brief ボタンを描画する
//-------------------------------------------------------------
void DarkButton::draw() {
	std::cout << "Darkテーマのボタンを描画\n";
}
LightButton.h
//-------------------------------------------------------------
//! @file   LightButton.h
//! @brief  Lightテーマ用のボタンの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Button.h"
//-------------------------------------------------------------
//! @class  LightButton
//! @brief  Lightテーマ用のボタン
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class LightButton : public Button {
public:
	//-------------------------------------------------------------
	// ボタンを描画する
	//-------------------------------------------------------------
	void draw() override;
};
LightButton.cpp
//-------------------------------------------------------------
//! @file	LightButton.cpp
//! @brief	Lightテーマ用のボタンの実装
//! @author つきの
//-------------------------------------------------------------
#include "LightButton.h"
#include <iostream>
//-------------------------------------------------------------
//! @brief ボタンを描画する
//-------------------------------------------------------------
void LightButton::draw() {
	std::cout << "Lightテーマのボタンを描画\n";
}
DarkText.h
//-------------------------------------------------------------
//! @file   DarkText.h
//! @brief  Darkテーマ用のテキストの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Text.h"
//-------------------------------------------------------------
//! @class  DarkText
//! @brief  Darkテーマ用のテキスト
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class DarkText : public Text {
public:
	//-------------------------------------------------------------
	// テキストを描画する
	//-------------------------------------------------------------
	void draw() override;
};
DarkText.cpp
//-------------------------------------------------------------
//! @file   DarkText.cpp
//! @brief  Darkテーマ用のテキストの実装
//! @author つきの
//-------------------------------------------------------------
#include "DarkText.h"
#include <iostream>
//-------------------------------------------------------------
//!@brief テキストを描画する
//-------------------------------------------------------------
void DarkText::draw() {
	std::cout << "Darkテーマのテキストを描画\n";
}
LightText.h
//-------------------------------------------------------------
//! @file   LightText.h
//! @brief  Lightテーマ用のテキストの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Text.h"
//-------------------------------------------------------------
//! @class  LightText
//! @brief  Lightテーマ用のテキスト
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class LightText : public Text {
public:
	//-------------------------------------------------------------
	// テキストを描画する
	//-------------------------------------------------------------
	void draw() override;
};
LightText.cpp
//-------------------------------------------------------------
//! @file   LightText.cpp
//! @brief  Lightテーマ用のテキストの実装
//! @author つきの
//-------------------------------------------------------------
#include "LightText.h"
#include <iostream>
//-------------------------------------------------------------
//! @brief テキストを描画する
//-------------------------------------------------------------
void LightText::draw() {
	std::cout << "Lightテーマのテキストを描画\n";
}
UIFactory.h
//-------------------------------------------------------------
//! @file   UIFactory.h
//! @brief  抽象的なUIファクトリのインターフェース定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include <memory>
// 前方宣言
class Button;
class Text;
//-------------------------------------------------------------
//! @class  UIFactory
//! @brief  抽象的なUIファクトリのインターフェース
//! @note   Abstract FactoryパターンのAbstractFactoryの役割を担う
//-------------------------------------------------------------
class UIFactory {
public:
	//-------------------------------------------------------------
	//! @brief  ボタンを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Button> createButton() const = 0;

	//-------------------------------------------------------------
	//! @brief  テキストを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Text> createText() const = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~UIFactory() = default;
};
DarkThemeFactory.h
//-------------------------------------------------------------
//! @file   DarkThemeFactory.h
//! @brief  Darkテーマ用のUIファクトリの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "UIFactory.h"
//-------------------------------------------------------------
//! @class  DarkThemeFactory
//! @brief  Darkテーマ用のUIファクトリ
//! @note   Abstract FactoryパターンのConcreteFactoryの役割を担う
//-------------------------------------------------------------
class DarkThemeFactory : public UIFactory {
public:
	//-------------------------------------------------------------
	// Darkテーマ用のボタンとテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Button> createButton() const override;

	//-------------------------------------------------------------
	// Darkテーマ用のテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Text> createText() const override;
};
DarkThemeFactory.cpp
//-------------------------------------------------------------
//! @file   DarkThemeFactory.cpp
//! @brief  Darkテーマ用のUIファクトリの実装
//! @author つきの
//-------------------------------------------------------------
#include "DarkThemeFactory.h"
#include "DarkButton.h"
#include "DarkText.h"
//-------------------------------------------------------------
//! @brief  Darkテーマ用のボタンとテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Button> DarkThemeFactory::createButton() const {
	return std::make_unique<DarkButton>();
}

//-------------------------------------------------------------
//! @brief  Darkテーマ用のテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Text> DarkThemeFactory::createText() const {
	return std::make_unique<DarkText>();
}
LightThemeFactory.h
//-------------------------------------------------------------
//! @file   LightThemeFactory.h
//! @brief  Lightテーマ用のUIファクトリの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "UIFactory.h"
//-------------------------------------------------------------
//! @class  LightThemeFactory
//! @brief  Lightテーマ用のUIファクトリ
//! @note   Abstract FactoryパターンのConcreteFactoryの役割を担う
//-------------------------------------------------------------
class LightThemeFactory : public UIFactory {
public:
	//-------------------------------------------------------------
	// Lightテーマ用のボタンとテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Button> createButton() const override;

	//-------------------------------------------------------------
	// Lightテーマ用のテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Text> createText() const override;
};
LightThemeFactory.cpp
//-------------------------------------------------------------
//! @file   LightThemeFactory.cpp
//! @brief  Lightテーマ用のUIファクトリの実装
//! @author つきの
//-------------------------------------------------------------
#include "LightThemeFactory.h"
#include "LightButton.h"
#include "LightText.h"
//-------------------------------------------------------------
//! @brief  Lightテーマ用のボタンとテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Button> LightThemeFactory::createButton() const {
	return std::make_unique<LightButton>();
}

//-------------------------------------------------------------
//! @brief  Lightテーマ用のテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Text> LightThemeFactory::createText() const {
	return std::make_unique<LightText>();
}
main.cpp
//-------------------------------------------------------------
//! @file   main.cpp
//! @brief  UI生成を模したAbstract Factoryパターンのサンプルコード
//! @author つきの
//-------------------------------------------------------------
#include "Button.h"
#include "Text.h"
#include "UIFactory.h"
#include "LightThemeFactory.h"
#include "DarkThemeFactory.h"
//エントリポイント
int main() {
	// UIファクトリのポインタを生成
	std::unique_ptr<UIFactory> factory;

	//-------------------------------------------------------------
	// LightテーマのUIを生成
	//-------------------------------------------------------------
	factory = std::make_unique<LightThemeFactory>();	// LightテーマのUIファクトリを生成
	auto btn1 = factory->createButton();				// Lightテーマのボタンを生成
	auto txt1 = factory->createText();					// Lightテーマのテキストを生成
	btn1->draw();										// ボタンを描画
	txt1->draw();										// テキストを描画

	//-------------------------------------------------------------
	// DarkテーマのUIを生成
	//-------------------------------------------------------------
	factory = std::make_unique<DarkThemeFactory>();		// DarkテーマのUIファクトリを生成
	auto btn2 = factory->createButton();				// Darkテーマのボタンを生成
	auto txt2 = factory->createText();					// Darkテーマのテキストを生成
	btn2->draw();										// ボタンを描画
	txt2->draw();										// テキストを描画
	//プログラムの終了	
	return 0;
}
result
Lightテーマのボタンを描画
Lightテーマのテキストを描画
Darkテーマのボタンを描画
Darkテーマのテキストを描画

クラス図

AbstractProduct/抽象製品

ConcreteProductが継承する共通インターフェースを定義します。
これを基底クラスに持つことで、Factoryが生成するProduct群としての整合性を保つことが可能になります。

Button.h
//-------------------------------------------------------------
//! @file   Button.h
//! @brief  抽象的なボタンのインターフェース定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
//-------------------------------------------------------------
//! @class  Button
//! @brief  抽象的なボタンのインターフェース
//! @note   Abstract FactoryパターンのAbstractProductの役割を担う
//-------------------------------------------------------------
class Button {
public:
	//-------------------------------------------------------------
	//! @brief  ボタンを描画する純粋仮想関数
	//! @note   派生クラスで実装をする
	//-------------------------------------------------------------
	virtual void draw() = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~Button() = default;
};
Text.h
//-------------------------------------------------------------
//! @file   Text.h
//! @brief  抽象的なテキストのインターフェース定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
//-------------------------------------------------------------
//! @class  Text
//! @brief  抽象的なテキストのインターフェース
//! @note   Abstract FactoryパターンのAbstractProductの役割を担う
//-------------------------------------------------------------
class Text {
public:
	//-------------------------------------------------------------
	//! @brief  テキストを描画する純粋仮想関数
	//! @note   派生クラスで実装をする
	//-------------------------------------------------------------
	virtual void draw() = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~Text() = default;
};

ConcreteProduct/具体製品

AbstractProductで定義されたインターフェースを実際に実装したクラスです。
テーマやプラットフォームごとの具体的な振る舞い(本記事ではDarkテーマとLightテーマ)を提供します。

DarkButton.h
//-------------------------------------------------------------
//! @file   DarkButton.h
//! @brief  Darkテーマ用のボタンの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Button.h"
//-------------------------------------------------------------
//! @class  DarkButton
//! @brief  Darkテーマ用のボタン
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class DarkButton : public Button {
public:
	//-------------------------------------------------------------
	// ボタンを描画する
	//-------------------------------------------------------------
	void draw()override;
};
DarkText.h
//-------------------------------------------------------------
//! @file   DarkText.h
//! @brief  Darkテーマ用のテキストの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Text.h"
//-------------------------------------------------------------
//! @class  DarkText
//! @brief  Darkテーマ用のテキスト
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class DarkText : public Text {
public:
	//-------------------------------------------------------------
	// テキストを描画する
	//-------------------------------------------------------------
	void draw() override;
};
LightButton.h
//-------------------------------------------------------------
//! @file   LightButton.h
//! @brief  Lightテーマ用のボタンの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Button.h"
//-------------------------------------------------------------
//! @class  LightButton
//! @brief  Lightテーマ用のボタン
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class LightButton : public Button {
public:
	//-------------------------------------------------------------
	// ボタンを描画する
	//-------------------------------------------------------------
	void draw() override;
};
LightText.h
//-------------------------------------------------------------
//! @file   LightText.h
//! @brief  Lightテーマ用のテキストの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "Text.h"
//-------------------------------------------------------------
//! @class  LightText
//! @brief  Lightテーマ用のテキスト
//! @note   Abstract FactoryパターンのConcreteProductの役割を担う
//-------------------------------------------------------------
class LightText : public Text {
public:
	//-------------------------------------------------------------
	// テキストを描画する
	//-------------------------------------------------------------
	void draw() override;
};

AbstractFactory/抽象ファクトリ

複数のProductをまとめて生成するためのインターフェースを定義するクラスです。
Factory Method単一のオブジェクト生成を扱うのに対し、Abstract Factory関連する複数のオブジェクトをセットとして生成することに特化しています。

UIFactory.h
//-------------------------------------------------------------
//! @file   UIFactory.h
//! @brief  抽象的なUIファクトリのインターフェース定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include <memory>
// 前方宣言
class Button;
class Text;
//-------------------------------------------------------------
//! @class  UIFactory
//! @brief  抽象的なUIファクトリのインターフェース
//! @note   Abstract FactoryパターンのAbstractFactoryの役割を担う
//-------------------------------------------------------------
class UIFactory {
public:
	//-------------------------------------------------------------
	//! @brief  ボタンを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Button> createButton() const = 0;
	
	//-------------------------------------------------------------
	//! @brief  テキストを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Text> createText() const = 0;

	//-------------------------------------------------------------
	//! @brief  仮想デストラクタ
	//-------------------------------------------------------------
	virtual ~UIFactory() = default;
};

生成関数の戻り値をAbstractProductにすることで、中身の差し替えを容易に行えます。

UIFactory.h
	//-------------------------------------------------------------
	//! @brief  ボタンを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Button> createButton() const = 0;
	
	//-------------------------------------------------------------
	//! @brief  テキストを生成する純粋仮想関数
	//-------------------------------------------------------------
	virtual std::unique_ptr<Text> createText() const = 0;

ConcreteFactory/具体ファクトリ

AbstractFactoryで定義されたインターフェースを実装し、特定のProduct群をまとめて生成するクラスです。
テーマ・プラットフォーム・設定などに応じて複数の ConcreteFactoryが存在し、それぞれが整合性の取れた製品の組み合わせを提供することで、差し替えの容易性や変更への強さを実現します。

LightThemeFactory.h
//-------------------------------------------------------------
//! @file   LightThemeFactory.h
//! @brief  Lightテーマ用のUIファクトリの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "UIFactory.h"
//-------------------------------------------------------------
//! @class  LightThemeFactory
//! @brief  Lightテーマ用のUIファクトリ
//! @note   Abstract FactoryパターンのConcreteFactoryの役割を担う
//-------------------------------------------------------------
class LightThemeFactory : public UIFactory {
public:
	//-------------------------------------------------------------
	// Lightテーマ用のボタンとテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Button> createButton() const override;

	//-------------------------------------------------------------
	// Lightテーマ用のテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Text> createText() const override;
};
LightThemeFactory.cpp
//-------------------------------------------------------------
//! @file   LightThemeFactory.cpp
//! @brief  Lightテーマ用のUIファクトリの実装
//! @author つきの
//-------------------------------------------------------------
#include "LightThemeFactory.h"
#include "LightButton.h"
#include "LightText.h"
//-------------------------------------------------------------
//! @brief  Lightテーマ用のボタンとテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Button> LightThemeFactory::createButton() const {
	return std::make_unique<LightButton>();
}

//-------------------------------------------------------------
//! @brief  Lightテーマ用のテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Text> LightThemeFactory::createText() const {
	return std::make_unique<LightText>();
}
DarkThemeFactory.h
//-------------------------------------------------------------
//! @file   DarkThemeFactory.h
//! @brief  Darkテーマ用のUIファクトリの定義
//! @author つきの
//-------------------------------------------------------------
#pragma once
#include "UIFactory.h"
//-------------------------------------------------------------
//! @class  DarkThemeFactory
//! @brief  Darkテーマ用のUIファクトリ
//! @note   Abstract FactoryパターンのConcreteFactoryの役割を担う
//-------------------------------------------------------------
class DarkThemeFactory : public UIFactory {
public:
	//-------------------------------------------------------------
	// Darkテーマ用のボタンとテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Button> createButton() const override;

	//-------------------------------------------------------------
	// Darkテーマ用のテキストを生成する
	//! @note   UIFactoryの純粋仮想関数をオーバーライドして実装
	//-------------------------------------------------------------
	std::unique_ptr<Text> createText() const override;
};
DarkThemeFactory.cpp
//-------------------------------------------------------------
//! @file   DarkThemeFactory.cpp
//! @brief  Darkテーマ用のUIファクトリの実装
//! @author つきの
//-------------------------------------------------------------
#include "DarkThemeFactory.h"
#include "DarkButton.h"
#include "DarkText.h"
//-------------------------------------------------------------
//! @brief  Darkテーマ用のボタンとテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Button> DarkThemeFactory::createButton() const {
	return std::make_unique<DarkButton>();
}

//-------------------------------------------------------------
//! @brief  Darkテーマ用のテキストを生成する
//-------------------------------------------------------------
std::unique_ptr<Text> DarkThemeFactory::createText() const {
	return std::make_unique<DarkText>();
}

#総括

  • Abstract Factoryパターンを使用することで、変更に強い形でまとまったオブジェクトの生成を可能にすることが出来る
  • テーマやプラットフォームの数だけConcreteFactoryConcreteProductが増えるので、クラスやファイル数が増えやすいというデメリットも存在している
  • AbstractFactory/抽象ファクトリが定義した生成関数の戻り値をAbstractProduct/抽象製品にすることで、変更やテストに強い形を実現している
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?