계산기 과제 중 트러블슈팅

권승용(Eric)·2024년 10월 31일

TIL

목록 보기
4/38

트러블슈팅 방법

  • 배경 : 어떤 현상을 발견해서
  • 발단 : 이런 장애가 생길 수 있다는 것을 인지했고,
  • 전개 : 장애를 대응, 해결하던 와중에
  • 위기 : 또 다른 장애 발견 또는 간단하게 해결할 수 없다는 것을 알게되어서,
  • 절정 : 근본적인 해결을 위해 이런 방법으로 접근하였다.
  • 결말 : 따라서, 이런이런 방법을 통해 근본적으로 해결 및 앞으로 유지, 보수에 용이하게 개선하게 되었다.

배경

  • 계산기 코드에서 각 연산 과정을 별도의 클래스로 분리해 단일 책임 원칙을 지키고자 하였다.
class AddOperation {
    func add(_ a: Double, _ b: Double) -> Double {
        return a + b
    }
}

class SubstractOperation {
    func substract(_ a: Double, _ b: Double) -> Double {
        return a - b
    }
}

class MultiplyOperation {
    func multiply(_ a: Double, _ b: Double) -> Double {
        return a * b
    }
}

class DivideOperation {
    func divide(_ a: Double, _ b: Double) -> Double {
        return a / b
    }
}

class RemainderOperation {
    func remainder(_ a: Double, _ b: Double) -> Double {
        guard b != 0 else {
            return .infinity
        }
        return a.truncatingRemainder(dividingBy: b)
    }
}

class Calculator {
    
    func calculate(operator op: String, firstNumber: Double, secondNumber: Double) -> Double {
        switch op {
        case "+":
            return AddOperation().add(firstNumber, secondNumber)
        case "-":
            return SubstractOperation().substract(firstNumber, secondNumber)
        case "*":
            return MultiplyOperation().multiply(firstNumber, secondNumber)
        case "/":
            return DivideOperation().divide(firstNumber, secondNumber)
        case "%":
            return RemainderOperation().remainder(firstNumber, secondNumber)
        default:
            return 0
        }
    }
}

발단

  • 위와 같은 코드 구조에서는 연산이 추가되거나 제거될 경우 Calculator 클래스도 함께 변경되기 때문에 유지보수성이 떨어진다는 문제점을 인지함.

전개

  • 인지한 문제점을 해결하기 위해 각 연산 클래스의 연산 함수를 추상화하기로 결정함.

위기

  • 추상화 방법을 고민함.

슈퍼클래스를 통한 추상화?

  • 부모 클래스의 코드를 재사용 가능함
  • 상속 계층 구조를 통해 일관된 구조를 제공
  • 단일 상속 제약이 있음
  • 유연성이 부족함

프로토콜을 통한 추상화?

  • 여러 프로토콜을 채택할 수 있는 유연성
  • 프로토콜을 채택한 각 구현이 독립적임
  • 구현 중복 가능성이 있음
  • 상속 관계를 표현하기 어려움
  • 공통 저장 속성 가질 수 없음

절정

  • 두 가지 방법을 고민해 본 결과, 현 문제를 해결하기 위해서는 프로토콜을 통한 추상화가 적합하다고 판단함.
  • 각 연산은 독립적이고, 각 연산 클래스는 공용 부모 클래스가 제공하는 기능이 필요없음.

결말

  • 프로토콜을 사용해 연산을 추상화하고, 함수를 실행하는 시점에 적합한 연산 역할의 클래스를 주입받아 연산을 수행하도록 구현하였다.
  • 이제 연산의 종류가 변경되어도 계산기 클래스에는 영향을 미치지 않는 유지보수성 좋은 코드가 되었다.
protocol AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double
}

class AddOperation: AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double {
        return a + b
    }
}

class SubstractOperation: AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double {
        return a - b
    }
}

class MultiplyOperation: AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double {
        return a * b
    }
}

class DivideOperation: AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double {
        return a / b
    }
}

class RemainderOperation: AbstractOperation {
    func calculate(_ a: Double, _ b: Double) -> Double {
        guard b != 0 else {
            return .infinity
        }
        return a.truncatingRemainder(dividingBy: b)
    }
}

class Calculator {
    
    func calculate(operation op: AbstractOperation, firstNumber: Double, secondNumber: Double) -> Double {
        op.calculate(firstNumber, secondNumber)
    }
}
profile
ios 개발자에용

0개의 댓글