swift로 계산기 만들다가 죽을 뻔한 썰 푼다.
더하기, 빼기, 곱하기, 나누기 연산을 수행할 수 있는 Calculator
클래스를 만들고, 이 클래스를 이용해 연산을 출력한다.
import Foundation
class Calculator {
func add(_ a: Double, _ b: Double) -> Double{
return a + b
}
func substract(_ a: Double,_ b: Double) -> Double {
return a - b
}
func multiply(_ a: Double, _ b: Double) -> Double {
return a * b
}
func divide(_ a: Double, _ b: Double) -> Double? {
return b != 0 ? a / b : nil
}
}
let calculator = Calculator()
let addResult = calculator.add(5, 3)
print(addResult)
let substractResult = calculator.substract(5, 3)
print(substractResult)
let multiplyResult = calculator.multiply(5, 3)
print(multiplyResult)
if let divideResult = calculator.divide(5, 3) {
print(divideResult)
} else {
print("0으로 나눔")
}
처음에는 모든 연산(더하기, 빼기, 곱하기, 나누기)을 하나의 큰 Calculator
클래스 안에서 모두 구현했다. 이 클래스는 각각의 연산을 위한 메서드(add
, subtract
, multiply
, divide
)를 가지고 있었다.
모든 연산을 한 클래스에 넣다 보니, 코드가 너무 복잡해지고 수정하기가 어려웠다. 특히 나중에 새로운 기능을 추가할 때 기존 코드에 영향을 줄 가능성이 높았다.
그래서 연산을 독립적인 클래스로 나눠야 했다. 이렇게 하면 각 클래스가 오직 하나의 역할만 하게 되어, 나중에 수정하거나 문제가 생겼을 때 더 쉽게 관리할 수 있을 것 같았다.
Calculator
클래스에 나머지 연산을 추가하고, 이를 통해 연산을 출력한다.
RemainderOperation
이라는 클래스를 추가했다. 이 클래스는 두 숫자를 나눌 때의 나머지를 계산한다.
class RemainderOperation {
func calculate(_ a: Double, _ b: Double) -> Double? {
return b != 0 ? a.truncatingRemainder(dividingBy: b) : nil
}
}
Calculator
클래스에 remainder
메서드를 추가하여 이 클래스를 사용하도록 했다.
truncatingRemainder(dividingBy:)
를 사용한 이유나머지 연산을 구현할 때, Swift에서는 truncatingRemainder(dividingBy:)
를 사용했다. 이 메서드는 정수뿐만 아니라 소수점이 있는 실수(Double 타입)에 대해서도 정확하게 나머지를 구할 수 있다.
일반적으로 정수에서는 %
연산자를 사용하여 나머지를 구할 수 있지만, 실수 연산에서는 이 연산자를 사용할 수 없다. 그래서 실수에서도 나머지를 계산할 수 있도록 truncatingRemainder(dividingBy:)
를 사용했다.
예를 들어 1.56 % 2
같은 연산을 수행할 때, truncatingRemainder(dividingBy:)
는 소수점까지 정확한 나머지 값을 계산해준다. 또한, 나누는 값이 음수일 때도 올바르게 결과를 반환해줘서 다양한 경우의 수를 처리하는 데 적합하다.
이 메서드를 사용함으로써 나머지 연산이 필요할 때 안전하고 정확하게 계산할 수 있었다.
0으로 나누기 문제가 여전히 있었다. 나누기와 마찬가지로, 나머지 연산에서도 0으로 나누는 경우를 처리해야 했다. 이를 위해 nil
을 반환하도록 하고, 호출하는 쪽에서 안전하게 처리하게 했다.
if let remainderResult = calculator.remainder(1.56, 0) {
print(remainderResult)
} else {
print("0으로 나눌 수 없습니다.")
}
이렇게 하면 0으로 나눌 때 nil
이 반환되고, 적절한 메시지를 출력할 수 있게 되었다.
각 연산을 담당하는 클래스를 만들고, 이 클래스들과 Calculator
클래스 간의 관계를 설정한다.
각 연산을 독립적인 클래스로 나누었다. 예를 들어, 더하기는 AddOperation
클래스, 빼기는 SubtractOperation
클래스에서 담당하도록 했다. 이렇게 하면 각 클래스의 역할이 분명해졌다.
class AddOperation {
func calculate(_ a: Double, _ b: Double) -> Double {
return a + b
}
}
class SubtractOperation {
func calculate(_ a: Double, _ b: Double) -> Double {
return a - b
}
}
그리고 Calculator
클래스는 각 연산 클래스를 멤버 변수로 가지고 있으며, 이 클래스를 사용해 실제 연산을 수행하게 했다.
class Calculator {
private let addOperation = AddOperation()
private let subtractOperation = SubtractOperation()
private let multiplyOperation = MultiplyOperation()
private let divideOperation = DivideOperation()
private let remainderOperation = RemainderOperation()
func add(_ a: Double, _ b: Double) -> Double {
return addOperation.calculate(a, b)
}
// 나머지 메서드들도 동일하게 각각의 클래스를 호출함
}
처음에는 모든 연산을 한 클래스에 넣으려고 했지만, 이렇게 하면 코드가 너무 복잡해지고 수정하기 어려웠다. 그래서 각 연산을 별도의 클래스로 나누고 Calculator
에서 이들을 사용하는 방식으로 책임을 분리했다. 이로 인해 유지보수성이 크게 향상되었고, 새로운 연산이 필요할 때 더 쉽게 추가할 수 있게 되었다.
그 뒤는 죽을 뻔 해서 못했다.
객체지향 프로그래밍을 배워야 겠다고 느꼈다.....
마지막으로 Calculator
클래스를 사용해 각 연산을 수행해 보겠다.
let calculator = Calculator()
let addResult = calculator.add(5, 3)
print(addResult) // 출력: 8.0
let subtractResult = calculator.subtract(5, 3)
print(subtractResult) // 출력: 2.0
let multiplyResult = calculator.multiply(5, 3)
print(multiplyResult) // 출력: 15.0
if let divideResult = calculator.divide(5, 3) {
print(divideResult) // 출력: 1.6666666666666667
} else {
print("0으로 나눔")
}
if let remainderResult = calculator.remainder(1.56, 2) {
print(remainderResult) // 출력: 1.56
} else {
print("0으로 나눔")
}
이렇게 각 연산을 수행하고, 0으로 나누는 경우에는 예외적으로 처리하여 프로그램이 안전하게 동작하도록 했다.
레벨 2에 글이 반복 돼요!
그리고 연산 메서드의 파라미터도 사용자가 구분하기 쉽게 firstOperand같은 이름을 짓는 건 어떨까요
계산기 내부에 직접 객체를 생성하셨는데 객체를 외부에서 전달받는 방법과 비교했을 때 어떤 차이가 잇을까여