はじめに
前回の記事「SwiftでSOLID原則(1/3)」 では、ソフトウェアデザインの中心である SOLID 原則 の概念を紹介し、特に「単一責任の原則」(SRP)について詳しく解説しました。この記事では、 オープン/クローズドの原則(OCP) に関して、Swiftを使った例で、紹介したいと思います。
オープン/クローズドの原則(OCP)
オープン/クローズドの原則は、「ソフトウェアのエンティティは、拡張には開かれ、変更には閉じているべき」と提唱しています。つまり、機能を追加したり改善する際、既存のコードを変更せずに対応できるようにモジュールを設計することが理想です。
概念的な例:
オープン/クローズドの原則は、建物の追加建築と似ています。新しい機能を追加したいとき、既存の構造物を壊すことなく、新しい部屋やブロックを加えられることが望ましいです。これにより、全体の構造が変わらず、新しい部分を追加するだけで新機能が実現されます。
Swiftでの実例:
例えば、幾何学的な形状を表現する Shape クラスがあるとします。新しい形(例:三角形や楕円)を追加したい場合、既存のShapeクラスのコードを直接変更するのではなく、新しいサブクラスを作成して追加機能を実現します。これはコードを拡張可能にし、バグの発生を最小限に抑える手法です。
では二つの例を見てみましょう
例1:
class Shape {
enum ShapeType {
case rectangle, triangle
}
var type: ShapeType
var width: Double = 0
var height: Double = 0
var base: Double = 0
init(type: ShapeType) {
self.type = type
}
func area() -> Double {
switch type {
case .rectangle:
return width * height
case .triangle:
return 0.5 * base * height
}
}
}
// 使用例
let rectangle = Shape(type: .rectangle)
rectangle.width = 10
rectangle.height = 5
let triangle = Shape(type: .triangle)
triangle.base = 8
triangle.height = 6
print("Rectangle area: \(rectangle.area())")
print("Triangle area: \(triangle.area())")
**解説:このコードでは、Shape
クラスが形状のタイプ (ShapeType
列挙型) を管理しており、area()
メソッドで形状に応じた処理を行っています。新しい形状(例えば、円や正方形)を追加したい場合、ShapeType
と area()
のコードを修正しなければならず、OCPに反する設計です。コードを直接変更するたびに、バグを引き起こすリスクも増えます。
例2:
// 定義されたShapeプロトコル
protocol Shape {
func area() -> Double
}
// 四角形のサブクラス
class Rectangle: Shape {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
func area() -> Double {
return width * height
}
}
// 新しい形状を追加(既存コードに影響なし)
class Triangle: Shape {
var base: Double
var height: Double
init(base: Double, height: Double) {
self.base = base
self.height = height
}
func area() -> Double {
return 0.5 * base * height
}
}
// 使用例
let shapes: [Shape] = [
Rectangle(width: 10, height: 5),
Triangle(base: 8, height: 6)
]
for shape in shapes {
print("Shape area: \(shape.area())")
}
**解説: この例では Shape
プロトコルを使用して、異なる形状クラス(Rectangle
や Triangle
)の面積を計算するための共通インターフェースを提供しています。新しい形状を追加しても、既存のコードを変更する必要がありません。
適用性:
OCPは、再利用可能で拡張可能なソフトウェアコンポーネントを設計する際に特に役立ちます。機能追加が求められるプロジェクトにおいて、既存のコードを保護しつつ拡張するための有効な手法です。
実装方法:
OCPを実現するために、インタフェース、プロトコル、または抽象クラスなどを用いて契約を定義します。この契約に基づく新しいクラスを作成し、OCPに従い、既存のコードの変更を避けながらも機能を追加します。
メリット:
- コードの再利用性が向上し、コードを変更することで発生する潜在的なバグのリスクが低減されます。
- ソフトウェアの保守性が向上します。
留意点:
この原則を適用するには、設計時点で先見の明が必要です。また、過度な抽象化は複雑さを招く可能性があるため、OCPの使用が適切かを見極めることが重要です。
結論:
ここまでで、SOLID原則の二つの重要な原則である「単一責任の原則」と「オープン/クローズドの原則」について掘り下げて学びました。次回の記事では、「リスコフの置換原則」 に注目し、Swiftでの具体的な実装例とともに、その効果とメリットを紹介します。次の記事まで、ハッピーコーディング!