[学習メモ]Pytho3で学ぶデザインパターン~生成に関するデザインパターン~
この記事について
生成に関するデザインパターン5種類について、個人の学習メモです。
(恥ずかしながら初勉強です・・・自身の整理のためにもまとめていこうと思います)
概要
デザインパターンについて
簡単にいうと設計のノウハウ集です。
OOPL(オブジェクト指向プログラミング)によって、ソフトウェアの再利用部品を作ることが可能になりました。クラスライブラリやフレームワークと呼ばれているものです。このクラスライブラリやフレームワークを作成する過程で、機能拡張が容易で再利用がしやすい先人たちの設計アイデアから共通部分を抜き出して(抽象化して)、設計パターンとしてまとめたものです。
ここでは広く一般的に知られている、優れた技術者4人GoF(ゴフ、ギャングオブフォー)が書いた「書籍:デザインパターン」にて発表されたGoFのデザインパターンについて扱います。
取り扱い注意事項
- 手段を目的にしない
目的(問題を解決したい) を解決する手段(デザインパターンを使用する) です。デザインパターンはよくある問題を解決する手段(糸口)です。 - デザインパターンを当てはめて問題を探さない(あら探し)
問題を探す手段ではなく解決手段です。 - 銀の弾丸はありません
GoFのデザインパターン
生成に関するデザインパターン <- この記事です。
- Factory Method
- Abstract Factory
- Builder
- Prototype
- Singleton
構造に関するデザインパターン
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
振る舞いに関するデザインパターン
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
生成に関するデザインパターン
そもそも、「生成に関するデザインパターン」って何でしょうか?
まずは、この言葉について知ります。
2より引用
生成に関するデザインパターンは、オブジェクトの生成方法に関連したデザインパターンです。
何も説明がいらないほどわかりやすいです。補足するならば、オブジェクトまたは関連したオブジェクトのグループのインスタンス化方法に焦点をあてているパターンです。
つまり、生成に関するデザインパターンとは 「オブジェクト生成関連で問題が起きているんだけど、どういう形でインタンス化するのがいいんだろうか?」といったことに対して解決する手段(糸口) をパターンとして示しています。
Factory Methodパターン
利用場面
4より引用
GoFによれば,Factory Method パターンの目的は, 「オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Method パターンは,インスタンス化をサブクラスに任せる。 」
2より引用
Factory Methodパターンが用いられるのは、あるオブジェクトが要求された時に、どのクラスをインスタンス化すべきかをサブクラスに選ばせたい場合である。さらに、前もってインスタンス化すべきクラスがわからない場合においても有効に利用できる。
利用シーンとしては以下のようです。
- 実行時に必要なサブクラスが動的に決定されるような場合
- クライアントが前もってインスタンス化すべきサブクラスがわからない場合(知る必要性がない場合も含む)
図で考えてみる
スポーツカー生産工場が誰かのスポーツカー(受注生産)を作るとします。
スポーツカー生産工場で一般車も作成することにしました。
ここで問題が起きました。スポーツカー生産工場にはスポーツカー作成担当しかいません。
スポーツカー作成担当は一般車の作成も可能でしょうか?(この例では出来ないとします)
技術的にも物理的にも生産不可能なことがわかったので、新たに一般車作成担当を雇うことにします。
担当を雇うことで問題が解決すると思った矢先。実は、スポーツカー生産工場は余剰の生産力がないという問題が見つかりました。
生産できないので新たに工場を増やすことにします。一般車を生産する工場を別に作ります。
一般車生産工場を0ベースの知見で作るのは時間の無駄です。スポーツカー生産工場から生産に必要な要素を抽象化してテンプレ工場とします。このテンプレ工場から一般車生産工場を作成します。
生産能力: スポーツカー制作工場、一般車制作工場の2つになりました。
あとは適切な工場に生産を支持するだけで良さそうです。
補足
目的に照らし合わせると
オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。
インターフェースというのは、この例でいうと「テンプレ工場」になります。テンプレ工場は現実に存在する工場ではなく工場に必要な機能をまとめた設計図みたいなものです。何を生産するかは工場が決めるという意味になります。
Factory Method パターンは,インスタンス化をサブクラスに任せる。 」
「スポーツカー制作工場、一般車制作工場」がサブクラスに該当しています。つまり、「各工場に生産する権限を渡してませよう」という考え方です。
作成方法
「インターフェースを書いておいて継承して使用する形にしておき、どのクラスをインスタンスするかはサブクラスが決めるようにする(サブクラスにインスタンス化する権限を渡して任せる)」です。
オブジェクト思考による作成方法のイメージ
Creatorは各工場のインターフェースのことです。Creatorはインターフェースですが生成処理の関数をもっています。(生産する権限のことです)
ConcreteCreator1、ConcreteCreator2は実際の工場です。Creatorを継承しています。create()することでスポーツカーが生成されます(ConcreteProduct1、ConcreteProduct2が生成される)
Produtは車という概念のインターフェースです。
ConcreteCreator1をインスタンス化してあげれば、継承元のCretorの生成する権限をもっていること(任せれたこと)になります。また、スポーツカー(ConcreteProduct1)を作成することはスポーツカー生産工場(ConcreteCreator1)が決めます。
これで目的を達成することができます。
「オブジェクトを生成するときのインタフェースだけを規定して,実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Method パターンは,インスタンス化をサブクラスに任せる。 」
*参考
- Creator: Productを生成する処理が書いてあるインターフェース
- ConcreteCreator1,ConcreteCreator2: Creatorの具像(ConcreteProductを作成するクラス)
- ConcreteProduct1,ConcreteProduct2: Productを具体化したクラス(具像)
- Product: 生成オブジェクトのインターフェース
python3で書いてみる
from abc import ABC,abstractclassmethod
class Factory(ABC):
'''Creatorに該当する
'''
def __init__(self):
self.registered_owner = []
def create(self,owner):
self.owner = owner
product = self.create_product(owner)
self.register_product(product)
return product
@abstractclassmethod
def create_product(self):
pass
@abstractclassmethod
def register_product(self):
pass
class Car(ABC):
'''Productに該当する
'''
@abstractclassmethod
def use(self):
pass
class SportsCar(Car):
'''ConcreteProduct1に該当する
'''
def __init__(self,owner):
self.owner = owner
def use(self):
print(f'{self.owner}のスポーツカーです')
class GeneralCar(Car):
'''ConcreteProduct2に該当する
'''
def __init__(self,owner):
self.owner = owner
def use(self):
print(f'{self.owner}の一般車です')
class SportsCarFactory(Factory):
'''ConcreteCreator1に該当する
'''
def create_product(self,owner):
return SportsCar(owner)
def register_product(self, product: SportsCar):
return self.registered_owner.append(product.owner)
class GeneralCarFactory(Factory):
'''ConcreteCreator2に該当する
'''
def create_product(self,owner):
return GeneralCar(owner)
def register_product(self, product: GeneralCar):
return self.registered_owner.append(product.owner)
sports_car_factory = SportsCarFactory()
# CarFactoryの継承元のCreate()を呼び出す
hoge_owner_car = sports_car_factory.create("HOGE")
hoge_owner_car.use()
fuga_owner_car = sports_car_factory.create("FUGA")
fuga_owner_car.use()
print(sports_car_factory.registered_owner)
general_car_factory = GeneralCarFactory()
hoge_owner_car = general_car_factory.create("PIYO")
hoge_owner_car.use()
fuga_owner_car = general_car_factory.create("POYO")
fuga_owner_car.use()
print(general_car_factory.registered_owner)
実行結果
HOGEのスポーツカーです
FUGAのスポーツカーです
['HOGE', 'FUGA']
PIYOの一般車です
POYOの一般車です
['PIYO', 'POYO']
Factory Methodを使用することで、柔軟にオブジェクトを作成することができるようになりました。
仮にタクシーを生産することになったら、「タクシー工場」と「タクシー構成」をインターフェースから作成してあげて、タクシー工場に生産を任せれば良いです。
Abstract Factoryパターン
利用場面
4より引用
部品を組み合わせて製品を作る場合,製品が違っても部品を替えるだけで組み立て方法が同じであれば,Abstract Factoryパターンが適用できる。
Abstract Factoryは文字通り抽象工場であり,抽象部品と抽象製品を持っています。そして工場,部品,製> 品をまとめて具象化するのです。別の製品をつくりたいときは,具象工場をそっくり取り換えるのです。
想像がつくと思うが,具象化すれば,工場,部品,製品は違うものだと言っても,部品の組み立て方法の構成は共> 通でなければなりません。ということは,部品を追加したりして組立の構成を替えるとすべての具象工場と抽象> 工場を更新しなければならなくなるのです。
引用だけでいいんじゃないか・・・・・・と思ってしまうほど説明がわかりやすいです。
利用シーンとしては以下のようです。
- 製品が違っても部品を替えるだけで組み立て方法が同じ場合で、作成ロジックが複雑になっている場合。
図で考えてみる
PC組み立て工場を例とします。
ミドルタワーPCを組み立てたいなら、ミドルタワー向けの部品を使えば良いということです。
「別の製品を作りたいなら具像工場を取り替える」なので、「ミドルタワーPC組み立て工場」「キューブ型PC組み立て工場」などと具像工場が増えるイメージです。
このようにな工程にしておけば、製品の作り方が変わらないのであれば具像製品を変更するだけで製品が作成できるということです。
作成方法
- 抽象的な工場から、具像工場を作成する
- 抽象製品(インタフェース)から、具像製品を作成する
- 具像工場は、具像製品を使って製品を作成する。別の製品を作りたいなら具像工場を取り替える。
オブジェクト思考による作成方法のイメージ
python3で書いてみる
factory.py: 具像工場、具像製品(メインモジュール兼ねる)
abstract_factory.py: 抽象工場、抽象製品
*オブジェクト思考による作成方法のイメージとの対応
- AbstractFactory -> Factory
- ConcreteFactory -> MiddleTowerFactory
- ConcreteProdcut1-1 -> MiddleTowerMotheBoard
- ConcreteProducut2-1 -> MiddleTowerPcCase
- Product1 -> MotheBoardItem
- Product2 -> PcCaseItem
import random
from abstract_factory import (MotheBoardItem,
PcCaseItem,
Factory)
class MiddleTowerMotheBoard(MotheBoardItem):
def __init__(self, name, maker):
super().__init__(name, maker)
self.id = random.randint(50,100)
def make_parts(self):
info = self.create()
return '商品ID:{}・マザーボード名:{}・メーカー名:{}。'.format(self.id,info["商品名"],info["メーカー名"])
class MiddleTowerPcCase(PcCaseItem):
def __init__(self, name, maker):
super().__init__(name, maker)
def make_parts(self):
info = self.create()
return 'PCケース名:{}・メーカー名:{}。'.format(info["商品名"],info["メーカー名"])
class MiddleTowerFactory(Factory):
def create_pc(self, motheboard, pc_case):
return print('PCが完成しました、内容:{}_{}'.format(motheboard,pc_case))
def create_base_motheboard(self, name, maker):
return MiddleTowerMotheBoard(name, maker)
def create_base_pc_case(self, name, maker):
return MiddleTowerPcCase(name, maker)
middle_tower_factory = MiddleTowerFactory()
gigabyte_base_motheboard = middle_tower_factory.create_base_motheboard('hoge','gigabyte')
gigabyte_motheboard = gigabyte_base_motheboard.make_parts()
asus_base_pc_case = middle_tower_factory.create_base_pc_case('fuge','ASUS')
asus_pc_case = asus_base_pc_case.make_parts()
middle_tower_factory.create_pc(gigabyte_motheboard,asus_pc_case)
from abc import ABCMeta, abstractmethod
class Item(metaclass = ABCMeta):
'''抽象製品(インターフェース)
'''
def __init__(self, name, maker):
self.name = name
self.maker = maker
@abstractmethod
def make_parts(self):
pass
class MotheBoardItem(Item):
'''マザーボード商品クラス(抽象的)
'''
def __init__(self, name, maker):
self.name = name
self.maker = maker
def create(self):
return {
'商品名': self.name,
'メーカー名': self.maker
}
class PcCaseItem(Item):
'''PCケース商品クラス(抽象的)
'''
def __init__(self, name, maker):
self.name = name
self.maker = maker
def create(self):
return {
'商品名': self.name,
'メーカー名': self.maker
}
class Factory(metaclass = ABCMeta):
'''抽象工場(インターフェース)
'''
@abstractmethod
def create_pc(self, name, maker):
pass
@abstractmethod
def create_base_motheboard(self, name, maker):
pass
@abstractmethod
def create_base_pc_case(self, name, maker):
pass
本来、マザーボード生成とPCケース生成においては材料も工程も違います。抽象製品においてcreateで一括して同じ処理書いてしまったけど、本来は違うものとして脳内補完してください。
ここにキューブ型を追加したい場合、factory.pyにクラスを定義するだけで良くなります。つまり、「別の製品を作りたいなら具像工場を取り替える」です。
Abstract FactoryとFactory Methodパターンの違い
実装で見るとオブジェクトの生成方法で違いがあります。
- Abstract Factoryパターンはクラスとして切り出します。
- Factory Methodパターンはメソッドで定義されています。
Abstract Factoryパターンは、Factory MethodパターンのFactoryメソッドに該当する部分を外部クラスに追い出すという点において、Factory Methodパターンよりオブジェクトの生成の柔軟性高いと思います(より疎結合ですし)
正直、理解でいません(違うものとは認識しているのですが)。どうしても、Abstract Factoryパターンの方が柔軟性があるのでFactory Methodの上位互換に感じてしまうんですよね。
Builderパターン
利用場面
4より引用
GoFによれば,Builder パターンの目的は, 「複合オブジェクトについて,その作成過程を表現形式に依存しないものにすることにより,同じ作成過程で異なる表現形式のオブジェクトを生成できるようにする。」
GoFによれば,Builder パターンは次のような場合に適用する。
・多くの構成要素からなるオブジェクトを生成するアルゴリズムを,構成要素自体やそれらがどのように組み合わされるのかということから独立にしておきたい場合。
・オブジェクトの作成プロセスが,オブジェクトに対する多様な表現を認めるようにしておかなければならない場合。
2より引用
Builderパターンが適しているケースは、その複雑なオブジェクトの内容(情報)を構成アルゴリズムとは切り離して、保持する必要がある場合である
4より引用
要は,複合オブジェクトから成り立つモジュールの利用者であるクライアントから見たときに非複合オブジェクトに見えるカプセル化(隠蔽)の方法である。
たくさんのオブジェクトから成り立つ一連の多様な作業を標準のアルゴリズムで実装し,個々の作業内容は別の独立したクラスに任すことができる。
5より引用
Wikipediaではこうです。
builderパターンとは、オブジェクト作成における「telescoping constructor」アンチパターンの解決を目指す意図を持つソフトウェアデザインパターンである。
利用シーンとしては
- 複雑なオブジェクトを構成する各部品(情報)を細かく作成しておいて、オブジェクトの作成過程を段階的にしたい場合です。
- 同じ作成過程なんだけど、表現が違うような場合。(M店のハンバーガーでピクルス抜くか抜かないかぐらいの表現の違い)
- 「telescoping constructor」アンチパターンの解決したい場合です。
補足
「telescoping constructor」アンチパターンとは、以下のようにコンストラクタで渡す引数が多くなっているという状況です。
class Hoge():
def __init__(self, hoge="",fuga="",piyo="",poyo=""・・・・・):
作成方法
- 「作る側(オブジェクト)と作られる生成物(オブジェクト)」をわけておく」
- 「作る側にインターフェースを持っておく」
オブジェクト思考による作成方法のイメージ
大工が現場監督に指示に従って建物を作るとします。
大工(Builder)のインタフェースから、継承してConcreteBuilderクラスが生まれます。ConcreteBuilderには日本家屋作り専門大工(ConcreteBuilder1)と、アパート作り専門の大工(ConcreteBuilder2)がいます。それぞれがcreate()するとProductが作成されます。
補足
- Director: Builderインタフェースを利用する人
- Builder: 作る側の処理が書いてあるインターフェース(抽象)
- ConcreteBuilder1,ConcreteBuilder2: 作る側。処理も具体的(具像)
- Product: 作られる生成物
python3で書いてみる
from abc import ABC, abstractclassmethod
class Builder(ABC):
'''抽象クラス(インターフェース)
'''
@abstractclassmethod
def build_base(self):
pass
@abstractclassmethod
def build_frame(self):
pass
@abstractclassmethod
def build_wall(self):
pass
class JapaneseStyleHouseBuilder(Builder):
def __init__(self, material:dict):
self.material = material
def build_base(self):
return '床材は{}で作成'.format(self.material["base"])
def build_frame(self):
return '柱は{}で作成'.format(self.material["frame"])
def build_wall(self):
return '壁は{}で作成'.format(self.material["wall"])
class ApartmentBuilder(Builder):
def __init__(self, material:dict):
self.material = material
def build_base(self):
return '床材は{}で作成'.format(self.material["base"])
def build_frame(self):
return '柱は{}で作成'.format(self.material["frame"])
def build_wall(self):
return '壁は{}で作成'.format(self.material["wall"])
class Director:
def construct(self, builder:Builder):
base = builder.build_base()
frame = builder.build_frame()
wall = builder.build_wall()
return print(f'家は{base}。{frame}。{wall}で完成')
japanese_style_material = {
"base" : "コンクリート",
"frame" : "木材",
"wall": "石膏"
}
apartment_material = {
"base" : "コンクリート",
"frame" : "鉄筋",
"wall": "石膏"
}
japanese_style_house = JapaneseStyleHouseBuilder(japanese_style_material)
apartment = ApartmentBuilder(apartment_material)
director = Director()
director.construct(japanese_style_house)
director.construct(apartment)
特に引っかかる部分はないかなと思います。
Directorのインスタンスからconstruct()メソッドを読み出して大工に仕事をしてもらいました。
Prototypeパターン
利用場面
4より引用
GoFによれば,Prototype パターンの目的は, 「生成すべきオブジェクトの種類を原型となるインスタンスを使って明確にし,それをコピーすることで新たなオブジェクトの生成を行う。」
2より引用
Prototypeパターンは、新しいオブジェクトをクローンにより作成する
利用シーンは
- オブジェクを0から作成しなおすより、既存のオブジェクトをコピーする(そして改変させたい)場合です。
クローンするメリット
例えば、MSのパワーポイントには「貼った図形をコピーする機能」があります。
図形をコピーする際はコピー元をPrototypeをとして登録しておいてからコピーすれば、クラスを0から作り直す手間がなく楽で早いです(パワーポイントがPrototypeを使用しているかは不明)
作成方法
- 新しいオブジェクトをクローンにより作成する
Pythonでのクローン方法は主に2つあります。
- copyモジュールのdeepcopy()を使用することでオブジェクトをクローンする
- オブジェクトからクラスオブジェクト(
__class__
)にアクセスしてオブジェクトを直接生成する
python3で書いてみる(deepcopy)
from abc import ABCMeta, abstractmethod
import copy
class Prototype(metaclass = ABCMeta):
@abstractmethod
def call(self, s):
pass
@abstractmethod
def create_clone(self):
pass
class Sheep(Prototype):
def __init__(self, name):
self.name = name
def call(self):
return print(self.name)
def create_clone(self):
copy_object = copy.deepcopy(self)
return copy_object
@property
def set_name(self):
return self.name
@set_name.setter
def clone_set_name(self,name):
self.name = name
merry = Sheep('メリーさん')
merry.call()
dolly = merry.create_clone()
dolly.name = 'ドリーさん'
dolly.call()
実行結果
メリーさん
ドリーさん
python3(__class__
)
from abc import ABCMeta, abstractmethod
class Prototype(metaclass = ABCMeta):
@abstractmethod
def call(self, s):
pass
@abstractmethod
def create_clone(self):
pass
class Sheep(Prototype):
def __init__(self, name):
self.name = name
def call(self):
return print(self.name)
def create_clone(self):
copy_object = self.__class__(self)
return copy_object
@property
def set_name(self):
return self.name
@set_name.setter
def clone_set_name(self,name):
self.name = name
merry = Sheep('メリーさん')
merry.call()
dolly = merry.create_clone()
dolly.name = 'ドリーさん'
dolly.call()
実行結果
メリーさん
ドリーさん
Singletonパターン
利用場面
5より引用
Real world example
There can only be one president of a country at a time. The same president has to be brought to action, whenever duty calls. President here is singleton.
In plain words
Ensures that only one object of a particular class is ever created.
(現実世界の例で、大統領は一国に一人しか存在しないから、職務上の要請がある場合は常に同じ大統領が動くことになる的なことが書いてます)
4より引用
クラスにインスタンスが1つしか存在しないことが重要になる場合がある。たとえば,システムには多数のプリンタを接続することができるが,プリンタスプーラは1つでなければならない。同様に,ファイルシステムやウィンドウマネージャも1つでなければならない。1つのデジタルフィルタは,1つの A/D コンバータを持つだろう。また,1つの会計システムは1つの会社の専用になるだろう。
2より引用
インスタンスがひとつだけ必要であり、そのインスタンスにプログラム全体からアクセスしたい場合、Singletonパターンが使われる
利用シーンは
- 特定のクラスのオブジェクトが1つだけ作成されることを保証したい場合です
作成方法
PythonでSingletonを実現する方法は複数存在します。
引用2には、「Python CookBook」を見ると色々あるよ!!という旨が書いてありましたが、サイトが重すぎて開けなかったので諦めました。
個人的に調べたものを載せます
- クラス変数と
__new__
を利用する - クラス変数と
__init__
とインスタンス生成関数を利用する
python3で書いてみる(1のパターン)
class Singleton():
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
s_1 = Singleton()
s_2 = Singleton()
print(id(s_1),id(s_2))
print(s_1==s_2)
実行結果(数字はメモリへのポインタなので実行するたびに変わります)
4531207904 4531207904
True
python3で書いてみる(2のパターン)
class Singleton2:
_instance = None
def __init__(self):
if self._instance is None:
Singleton2._instance = self
@staticmethod
def get_instance():
if Singleton2._instance == None:
Singleton2()
return Singleton2._instance
s_2_1 = Singleton2.get_instance()
s_2_2 = Singleton2.get_instance()
print(id(s_2_1),id(s_2_2))
print(s_2_1==s_2_2)
実行結果(数字はメモリへのポインタなので実行するたびに変わります)
4531207808 4531207808
True
状況によりますが、__new__
使う方が個人的には好きかもです。
まとめ
パターン名称 | 概要 | 利用シーン |
---|---|---|
Factory Method | サブクラスにインスタンス化する権限を渡して任せる | 実行時に必要なサブクラスが動的に決定されるような場合。クライアントが前もってインスタンス化すべきサブクラスがわからない場合(知る必要性がない場合も含む) |
Abstract Factory | 複数の部品を組み合わせて作成する | 製品が違っても部品を替えるだけで組み立て方法が同じ場合で、作成ロジックが複雑になっている場合 |
Builder | 複雑なインスタンスを段階を経て組み立てる | 複雑なオブジェクトを構成する各部品(情報)を細かく作成しておいて、オブジェクトの作成過程を段階的にしたい場合。同じ作成過程なんだけど、表現が違うような場合。「telescoping constructor」アンチパターンの解決したい場合 |
Prototype | 新しいオブジェクトをクローンにより作成する | オブジェクを0から作成しなおすより、既存のオブジェクトをコピーする(そして改変させたい)方が楽で早い場合 |
Singleton | 特定のクラスのオブジェクトが1つだけ作成されることを保証する | 特定のクラスのオブジェクトが1つだけ作成されることを保証したい場合 |
感想
今回は「生成に関するデザインパターン5種類」をPython3を使用しつつ理解してみるチャレンジを行いました。初めてデザインパターンに触りましたが、こういったものを一度で理解するのは難しいと割り切って60点くらいの出来を目指しております。
デザインパターンを使用することを目的とせずに、これからも精進していければと思います。
主に参考にさせていただいたもの(学習教材)
Author And Source
この問題について([学習メモ]Pytho3で学ぶデザインパターン~生成に関するデザインパターン~), 我々は、より多くの情報をここで見つけました https://zenn.dev/kalman/articles/4d5549baf1fcd5著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol