これは簡単な例ですが、SOLID私がよく苦労している原則との間の緊張を反映しています。
開閉原理の一般的な例(例 [1] 、 [2] )は、多くのShapeクラスがあり、drawShape()
メソッド、例:
// Open-Close Principle - Bad example
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type==1)
drawRectangle(s);
else if (s.m_type==2)
drawCircle(s);
}
public void drawCircle(Circle r) {....}
public void drawRectangle(Rectangle r) {....}
}
Open/Closed Principle(OCP)を使用すると、追加するすべての新しい図形に対してこのコードを変更する必要があることがわかります。変更の必要性をなくし、新しい動作を追加するためのより良い方法として拡張を許可します。
この例では、OCP準拠のソリューションとして以下を提供します。
// Open-Close Principle - Good example
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
class Shape {
abstract void draw();
}
class Rectangle extends Shape {
public void draw() {
// draw the rectangle
}
}
要約でOCPのデモンストレーションとして、これは良い例です。ただし、この解決策は、単一責任原則(SRP)の露骨な違反と私に思われます。私たちは、「描画の責任」を独自のクラスに分けて開始し、形状の単純な定義でその「描画の責任」をひとまとめにしてOCP問題を解決しました。
たとえば、描画にGUIとの相互作用が含まれる場合、この例では、単純な形状の定義とGUI操作を組み合わせます。それは間違っているようです。
OCPおよびからSRPに準拠する、ここでの良い再設計は何ですか?
キャンバスに描画します。描画では、UIでinteractionを必要としないでください。描画する図形をUIに転送して表示するだけです。
interface Shape {
void draw(Canvas canvas);
}
class GraphicEditor {
private Canvas mainCanvas;
void drawShape(Shape s, Point location) {
Canvas subCanvas = mainCanvas.subCanvas(location, s.width, s.height);
s.draw(subCanvas);
mainCanvas.copy(subCanvas, location);
}
}
より一般的な答え:
副作用ではなく、データを転送するメソッドを設計します。 (キャンバスのような)DTOを定義し、それらを階層に渡します。
すべてのオブジェクトには共同作業者がいます。それらを、親オブジェクトが情報を取得し、そのコア責任(SRP)の一部ではないタスクを実行するために使用するサービスと考えてください。結果のDTOは、これらのサービスの「製品」です。親オブジェクトは、これらの製品を好きなように操作できます(画面上での描画、ネットワーク経由での送信、DBへの保存など)。