관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Clean Code(클린 코드) - 5. 객체와 자료 구조 (객체지향, 절차지향 장단점) 본문

Clean Code (클린 코드)

[iOS - swift] Clean Code(클린 코드) - 5. 객체와 자료 구조 (객체지향, 절차지향 장단점)

jake-kim 2021. 11. 17. 01:14

Interface를 사용해야 하는 이유

  • Interface는 swift에서 protocol을 의미
  • 추상화 - 외부에서 구현체의 변수에 의존하지 않도록하기 위함
  • 인터페이스는 자료 구조를 명백하게 표현

WRONG

ex) Interface가 아닌 경우 - 구현을 외부에 노출

외부에서 구현체를 알아야하는지? 외부에서는 직교 좌표계인지, 극 좌표계인지 알 필요없이 값만 필요한 상황이므로 불필요한 정보를 외부에 노출하여, 외부에서 데이터를 사용할 때 불필요한 고민을 주는 상황

class Point {
    var x: CGFloat
    var y: CGFloat
    
    init(x: CGFloat, y: CGFloat) {
    	self.x = x
        self.y = y
    }
}

RIGHT

ex) Interface인 경우 - 구현을 외부에 감추는 형태

외부에 직교좌표계인지, 극좌표계인지에 관한 불필요한 구현내용을 보여주지 않고 필요한 것들만을 명시하여 자료 구조를 명백하게 표현

protocol Point {
    func getX() -> CGFloat
    func getY() -> CGFloat
    setCartesian(x: CGFloat, y: CGFloat)
    func getR() -> CGFloat
    func getTheta() -> CGFloat
    setPolar(r: CGFloat, theta: CGFloat)
}

추상화 시킨다는 의미

  • interface를 이용하는 것도 추상화 시키는 것이지만, 완전히 추상화 시킨다면 사용하는 쪽에서 구현체의 의미를 모르게 하는것이 중요

WRONG

ex) 사용하는쪽에서 구체적인 숫자값을 알게되는 경우

protocol Vehicle {
    getFuelTankCapacityInGallons() -> CGFloat
    getGallonsOfGasoline() -> CGFloat
}

RIGHT

ex) 외부에서 사용할 때 정보가 어디서 들어오는지 감추고, 추상적인 값인 백분율을 반환

- 정보를 세세하게 표현하지 말고 추상적인 개념으로 표출하는것이 베스트

protocol Vehicle {
    getPercentFuelRemaining() -> CGFloat
}

절차지향 프로그래밍 vs 객체지향 프로그래밍

> 핵심: 두 프로그래밍 모두 장단점이 있으므로 절대적으로 좋은 프로그래밍은 존재하지 않는 것을 인지

  • 장점
    • 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽고, 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉬운 형태
  • 단점
    • 절차적인 코드는 새로운 자료구조를 추가하기가 어렵고, 객체 지향 코드는 새로운 함수를 추가하기 어려움 (함수 하나가 추가되면 그를 상속받고 있는 클래스들에도 반영 필요)

ex) 절차지향 프로그래밍

- 장점: Geometry 클래스에 둘레 길이를 구하는 perimeter() 함수 추가 시, Square, Rectangle, Circle 클래스에 아무런 영향을 주지 않는

- 단점: 새 도형을 추가하고 싶다면, Geometry클래스에 속한 함수 내용들을 수정 필요

// 설명을 위해 init 생략
class Square {
    let topLeft: Point
    let side: Double
}

class Rectangle {
    let topLeft: Point
    let height: Double
    let width: Double
}

class Circle {
    let center: Point
    let radius: Double
}

class Geometry {
    let pi = 3.14
    
    func area(shape: AnyObject) -> Double {
        if let shape = shape as? Square {
            return shape.side * shape.side
        } else if let shape = shape as? Reactangle {
            return shape.height * shape.width
        } else if let shape = shape as? Circle {
            return PI * shape.radius * shape.radius
        } else {
            return -1
        }
    }   
}

ex) 객체지향 프로그래밍

- 장점: area()는 다형성을 띄고 있으므로, Geometry 클래스가 별도로 필요하지 않는 장점

- 단점: 새 함수를 추가하고 싶다면 도형 클래스 전부 수정 필요

class Square: Shape {
    private let topLeft: Point
    private let side: Double
    
    public override func area() -> Double {
        return side * side
    }
}

class Rectangle: Shape {
    private let topLeft: Point
    private let height: Double
    private let width: Double
    
    func override area() -> Double {
        return height * width
    }
}

class Circle: Shape {
    private let center: Point
    private let radius: Double
    public pi = 3.14
    
    public area() -> Double {
        return pi * radius * radius
    }
}

디미터 법칙 (Law of Demeter)

  • 의미: 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙
  • 객체 지향 방식에서, 객체는 자료를 숨기고 함수를 공개하는 방식

DTO (Data Transfer Object)

  • 공개 변수만 있고 함수가 없는 구조체
  • DB와 통신, API 통신에서 사용되는 모델
  • DB, API에서 저장된 가공되지 않은 정보를 애플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체
public struct Address {
    public private(set) let street: String
    public private(set) let streetExtra: String
    public private(set) let city: String
}

객체의 핵심

  • 공작을 공개(public)하고 자료를 숨김으로써 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기 어려운 구조
    • 모듈을 구현할 때 새로운 자료 타입을 추가하는 유연성이 필요할 땐 객체 사용
    • 새로운 동작을 추가하는 유연성이 필요하다면 절차적인 코드가 더욱 적합

* 참고: Clean Code (로버트 C. 마틴)

Comments