4. クラスの依存関係の例
class Actor {
int hp, mp;
string name;
}
ActorはBattleが
なくても動作する
class Battle {
int turnCount;
Actor[] actors;
}
BattleはActorが
ないと動作しない
5. クラスの依存関係の例
class Actor {
int hp, mp;
string name;
}
ActorはBattleが
なくても動作する
BattleがActorに依存していると表現される
class Battle {
int turnCount;
Actor[] actors;
}
BattleはActorが
ないと動作しない
Actor Battle
68. LCOMを用いたクラスの設計の検討
class PlayerParam {
HP hp;
int mp;
public void Recover() {
hp.Recover();
mp = MpMax;
}
}
LCOMの考え方に即した設計になった!
class HP { // HPの処理を切り出す
int hp;
public bool Alive => hp > 0;
public void Recover() => hp = HpMax;
}
78. Doorの機能を呼び出すクラスを考える
class Door {
public void Open();
public void Close();
public void OnButtonPushed()
=> Open();
public void OnTimeout()
=> Close();
}
class ButtonEvent {
Door door;
// ボタンを押したらDoorが開く
void OnPushed()
=> door.OnButtonPushed();
}
class TimerEvent {
Door door;
// 時間経過でDoorが閉じる
void OnTimeout()
=> door.OnTimeout();
}
79. Doorの機能を呼び出すクラスを考える
class Door {
public void Open();
public void Close();
public void OnButtonPushed()
=> Open();
public void OnTimeout()
=> Close();
}
class ButtonEvent {
Door door;
// ボタンを押したらDoorが開く
void OnPushed()
=> door.OnButtonPushed();
}
class TimerEvent {
Door door;
// 時間経過でDoorが閉じる
void OnTimeout()
=> door.OnTimeout();
}
具体への依存
具体への依存
80. Doorの機能を呼び出すクラスを考える
class ButtonEvent {
IPushedTimeout pushed;
void OnPushed()
=> pushed.OnButtonPushed();
}
class TimerEvent {
IPushedTimeout timeout;
void OnTimeout()
=> timeout.OnTimeout();
}
抽象への依存になり
IPushedTimeoutを実装すれば
置き換えられるようになった
interface IPushedTimeout {
public void OnButtonPushed();
public void OnTimeout();
}
class Door: IPushedTimeout {
public void Open();
public void Close();
public void OnButtonPushed()
=> Open();
public void OnTimeout()
=> Close();
}
81. Buttonの入力を受け取るクラスを新しく実装する
class GimmickSwitch: IPushedTimeout {
public void OnButtonPushed()
=> StartGimmick();
// インターフェースに実装を強制される
public void OnTimeout()
=> throw new NotSupportedException();
}
ButtonEventに必要ない
OnTimeout()を実装しなくては
ならなくなってしまった
interface IPushedTimeout {
public void OnButtonPushed();
public void OnTimeout();
}
class ButtonEvent {
IPushedTimeout pushed;
void OnPushed()
=> pushed.OnButtonPushed();
}
82. Buttonの入力を受け取るコンポーネントを実装する
class GimmickSwitch: IPushedTimeout {
public void OnButtonPushed()
=> StartGimmick();
// インターフェースに実装を強制される
public void OnTimeout()
=> throw new NotSupportedException();
}
さらにTimerEventから利用すると
IPushedTimeoutを実装しているのに
例外が投げられてしまう
interface IPushedTimeout {
public void OnButtonPushed();
public void OnTimeout();
}
class TimerEvent {
IPushedTimeout timeout;
void OnTimeout()
=> timeout.OnTimeout();
}
88. class Door: ITimeout, IPushed {
public void Open();
public void Close();
public void OnButtonPushed()
=> Open();
public void OnTimeout()
=> Close();
}
ところで
89. class Door: ITimeout, IPushed {
public void Open();
public void Close();
public void OnButtonPushed()
=> Open();
public void OnTimeout()
=> Close();
}
ドアの機能ではない
メソッドを持っています
ドアは外部にButtonやTimerがあることを知るべきでない
91. class Door {
public void Open();
public void Close();
}
IPushedとITimeoutをサポートするクラスを作る
class DoorPushed: IPushed {
Door door;
public void OnButtonPushed()
=> door.Open();
}
class DoorTimeout: ITimeout {
Door door;
public void OnTimeout()
=> door.Close();
}
92. class Door {
public void Open();
public void Close();
}
IPushedとITimeoutをサポートするクラスを作る
class DoorPushed: IPushed {
Door door;
public void OnButtonPushed()
=> door.Open();
}
class DoorTimeout: ITimeout {
Door door;
public void OnTimeout()
=> door.Close();
}
interface ITimeout {
public void OnTimeout();
}
class TimerEvent {
ITimeout timeout;
void OnTimeout()
=> timeout.OnTimeout();
}
interface IPushed {
public void OnButtonPushed();
}
class ButtonEvent {
IPushed pushed;
void OnPushed()
=> pushed.OnButtonPushed();
}
橋渡しのクラスを設けることで
Doorが自身の機能に集中しながら
インターフェースに対応できた
93. class Door {
public void Open();
public void Close();
}
Adapter
class DoorPushed: IPushed {
Door door;
public void OnButtonPushed()
=> door.Open();
}
class DoorTimeout: ITimeout {
Door door;
public void OnTimeout()
=> door.Close();
}
interface ITimeout {
public void OnTimeout();
}
class TimerEvent {
ITimeout timeout;
void OnTimeout()
=> timeout.OnTimeout();
}
interface IPushed {
public void OnButtonPushed();
}
class ButtonEvent {
IPushed pushed;
void OnPushed()
=> pushed.OnButtonPushed();
}
委譲による分離
橋渡しするクラスを と呼ぶ
と呼ぶこれを
97. 長方形クラスを継承した正方形を実装
class Rect { // 長方形
public virtual int Height { get; set; }
public virtual int Width { get; set; }
// Rectの実装を
// そのままSquareで利用可能!
public int Area => Height * Width;
public int InternalAngleSum
=> 90 * 4; // 内角の和を返す
/* … */
}
class Square: Rect { // 正方形は長方形
public override int Height {
set {
base.Height = value;
// 高さと幅を合わせる
if (Width != value) Width = value;
}
}
public override int Width {
set { /* Height同様の実装(省略) */ }
}
// Area プロパティを実装する必要がない!
// InternalAngleSumもRectで実装済
}
98. テストを書いてみる
Rect rect = new Rect();
rect.Height = 3;
rect.Width = 5;
Assert.Equal(15, rect.Area); // OK!
105. class Rect { // 長方形
public virtual int Height { get; set; }
public virtual int Width { get; set; }
// Rectの実装を
// そのままSquareで利用可能!
public int Area => Height * Width;
public int InternalAngleSum
=> 90 * 4; // 内角の和を返す
/* … */
}
class Square: Rect { // 正方形は長方形
public override int Height {
set {
base.Height = value;
// 高さと幅を合わせる
if (Width != value) Width = value;
}
}
public override int Width {
set { /* Height同様の実装(省略) */ }
}
}
修正箇所の割り出しに
基底クラスと派生クラスの両方を見る必要がある
108. 多態性はインターフェースで実現できる
class Worker: IWorker {
public int Work() {
Health -= 100;
return 1;
}
}
class WorkBot: IWorker {
public int Work() {
Energy -= 1;
return 100;
}
}
var workers = new IWorker[] {
new Worker(), new WorkBot()
};
var outputSum = 0;
foreach(var worker in workers) {
outputSum += worker.Work();
}
ポリモーフィズム
109. 機能の共通化は包含で実現できる
class Worker: IWorker {
private PC pc; // 仕事効率化処理を提供
public int Work() {
Health -= 100;
return pc.PromoteTask(1);
}
}
class WorkBot: IWorker {
private PC pc;
public int Work() {
Energy -= 1;
return pc.PromoteTask(100000000);
}
}
var workers = new IWorker[] {
new Worker(), new WorkBot()
};
var outputSum = 0;
foreach(var worker in workers) {
outputSum += worker.Work();
}
コンポジション