타입별 필요할 것으로 예상하는 프로퍼티와 메서드를 작성하였다. 추가 보완할 점은 아래 사항들이 있겠다.
PlusAndMinus
프로토콜과 익스텐션 프로퍼티와 메서드 작성Stack<T>
의 list
를 변경하거나 변경하는 메서드를 호출하는 메서드에 대해 mutating
키워드를 작성해주는 것추가로 고민해볼 점은 십진 계산기와 이진 계산기 타입의 경우 애플리케이션에서 여러 인스턴스를 만들 여지가 없으니 내부 요소들을 타입 메서드와 타입 프로퍼티로 선언하는 것이 있겠다.
계산기 프로젝트에서 한 가지의 요구사항으로 스택을 직접 구현하였다. 반드시 이진수 계산기와 십진수 계산기 타입이 있을 것이므로 각 계산기에서 주로 사용하는 타입 (Double
및 Int
)에 맞게 범용적으로 사용할 수 있는 Stack을 구현하려면 제네릭 타입으로 구현하는 것이 좋다고 판단했다.
struct Stack<T> {
private var list = [T]()
var isEmpty: Bool {
return self.list.isEmpty
}
var top: T? {
return self.list.last
}
mutating func push(_ item: T) {
self.list.append(item)
}
mutating func pop() -> T? {
return self.list.popLast()
}
mutating func reset() {
self.list.removeAll()
}
}
실제로 계산을 구현할 때는 top
연산프로퍼티에서 반환되는 값과 새로 사용자가 입력하는 값을 통해 연산하고, push(_:)
메서드를 통해 새로운 계산 결과를 스택에 넣어주는 형식으로 활용할 것이다. pop()
메서드는 활용처를 생각해보지 않았는데 스택의 기본기능이라 구현해보았다. 프로젝트를 진행하면서 활용처를 고민해보아야겠다.
객체지향 프로그래밍 패러다임에 맞는 프로그래밍을 하기 위한 5가지 원칙이다.
SOLID 원칙은 아래의 가치를 지키기 위한 한 가지 수단이라는 점을 잊지 말아야 한다. 프로그래밍 디자인 패턴과 마찬가지로 더 나은 객체지향 프로그래밍을 하기 위한 수단일 뿐이다. 이들을 따르더라도 더 좋은 결과물을 만들 수 있다는 것을 보장하지 않으며 너무 원칙에 끼워맞추려 할 경우 오히려 아래와 같은 가치를 훼손시키는 본말전도의 사례가 될 수 있다.
다섯 가지 원칙에 대해 별도로 자세히 포스팅한다면 좋을 것 같다. 포스팅하면 링크 예정.
한 클래스는 한 가지의 책임만을 가져야 한다.
Note: 응집도는 관련성 있는 코드들이 얼마나 잘 묶여있는지를, 결합도는 불필요한 의존성이 있는지를 나타내는 척도이다.
자식 클래스는 부모 클래스로써의 역할을 완벽히 수행할 수 있어야 한다.
struct
, class
, enum
과 같은 타입은 제네릭 타입을 적용할 수 있는 반면, 프로토콜은 그렇지 못하다. 이유를 알고 싶다. 어떻게 찾아보면 될까.. why does protocol not allow generic type in swift 구글링..?
역시 구글링하니 뭔가 나오는군.. Swift Programming Language의 Generic 챕터의 Associated Types
부분을 읽어보면 답이 나올 것 같다..! 현재 예상하는 바로는 associatedtype
을 이용하면 한 가지 타입을 선택해야 하는 제네릭과 달리 프로토콜에서 요구하는 프로퍼티나 메서드를 각자 원하는 타입으로 자유롭게 설정이 가능할 것 같다.
protocol UserInputConvertible {
associatedtype T
func userInput() -> T
// 채택하는 타입에서 T를 자유롭게 설정할 수 있다.
// 제네릭 타입처럼 어떠한 타입으로 지정하여도 프로토콜을 준수한다고 판단한다.
}
struct UserInputToString: UserInputConvertible {
func userInput() -> String? { // 프로토콜의 T 타입을 String? 타입으로 적용하여도 프로토콜을 준수함.
return readLine()
}
}
struct UserInputToInt: UserInputConvertible {
func userInput() -> Int? { // 프로토콜의 T 타입을 Int? 타입으로 적용하여도 프로토콜을 준수함.
guard let _userInput = readLine() else { return nil }
return Int(_userInput)
}
}