
자료 / 객체 비대칭 (119p)
- 자료를 세세하게 공개하기보다는 추상적인 개념으로 표현하는 편이 좋다.
- 절차적인 코드
- 각 클래스에는 아무런 메소드도 제공하지 않으며 대신 모든 클래스의 동작을 관리하는 클래스가 존재한다. 이 방식이 절차적인 방식의 코드이다.
- 장점: 모든 클래스의 동작을 관리하는 클래스 내에 새로운 함수를 추가하기 쉽다.
- 단점: 만일 새로운 클래스를 추가하면 모든 클래스의 동작을 관리하는 클래스 내부의 함수를 전부 다 고쳐야 한다.
- 새로운 객체가 아니라 새로운 함수가 필요하게 된 경우, 절차적인 코드 기법이 더 적합하다.
struct Point {
var x: Double
var y: Double
}
struct Square {
var topLeft: Point
var side: Double
}
struct Rectangle {
var topLeft: Point
var height: Double
var width: Double
}
struct Circle {
var center: Point
var radius: Double
}
enum Shape {
case square(Square)
case rectangle(Rectangle)
case circle(Circle)
}
enum GeometryError: Error {
case NoSuchShapeException
}
class Geometry {
final var PI: Double = 3.141592653589793
func area(of shape: Shape) throws -> Double {
switch shape {
case .square(let square):
return square.side * square.side
case .rectangle(let rectangle):
return rectangle.height * rectangle.width
case .circle(let circle):
return PI * circle.radius * circle.radius
default:
throw GeometryError.NoSuchShapeException
}
}
}
- 객체 지향 코드 (프로토콜 지향 코드)
- 프로토콜을 이용해 다형적인 메소드를 구현한 뒤, 각각의 클래스 내부에서 동작하도록 만든다. 이는 각각의 클래스가 동작하는 방식을 하나의 클래스에서 모두 구현하는 방식과는 확연히 다르다.
- 장점: 새 객체를 추가해도 문제 없음. 그냥 새 객체가 프로토콜을 채택하고 내부 구현은 알아서 하면 되기 때문.
- 단점: 새 함수를 프로토콜 내에 추가하게 된다면, 프로토콜을 채택한 모든 클래스 전부를 고쳐야 한다.
- 복잡한 코드를 만들다가 객체를 새롭게 생성해야 할 경우가 생긴다. 이 때에는 객체 지향 코드 기법이 가장 적합하다.
import Foundation
struct Point {
var x: Double
var y: Double
}
protocol Shape {
func area() -> Double
}
struct Square: Shape {
var topLeft: Point
var side: Double
func area() -> Double {
return side * side
}
}
struct Rectangle: Shape {
var topLeft: Point
var height: Double
var width: Double
func area() -> Double {
return height * width
}
}
struct Circle: Shape {
var center: Point
var radius: Double
let PI = Double.pi
func area() -> Double {
return PI * radius * radius
}
}
- 분별 있는 프로그래머는 모든 것이 객체라는 생각이 미신임을 잘 안다. 풀어서 설명하면 모든 것을 프로토콜화 시켜서 코드를 짜려는 생각이 미신임을 잘 안다. 때로는 단순한 자료 구조와 절차적인 코드가 적합한 상황도 있다.
디미터 법칙 (123p)
- 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙.
- 디미터 법칙을 준수하려면 객체가 단순하게 '요청'을 하고, 그에 따른 '응답'을 받는 것으로 상호작용을 제한해야 함. 다시 말해, 객체 A가 객체 B의 메소드를 호출하면, 객체 B는 결과값을 반환하거나, 필요한 작업을 수행하고 완료하여 객체 A에게 응답해야 한다. 이런 방식으로 객체의 내부 사정을 외부가 모르게 할 수 있다.
class House {
var door: Door?
}
class Door {
var isLocked: Bool = false
}
let myHouse = House()
if let door = myHouse.door, let lock = door.lock {
lock.isLocked = true
}
==============================================================================
class House {
var door: Door?
func openTheDoor() {
door.unlockTheDoor()
}
}
class Door {
var isLocked: Bool = false
func unlockTheDoor() {
isLocked = true
}
}
let myHouse = House()
myHouse.openTheDoor()
결론 (127p)
- 새로운 객체 및 자료 타입을 추가하는 유연성이 필요하다면 프로토콜 지향 코드가 필요하다.
- 반면 새로운 동작을 추가 (새로운 함수를 생성)하는 유연성이 필요하다면 자료 구조와 절차적인 코드가 더 적합하다.