엘레강트 오브젝트

sanghoon Ahn·2022년 4월 22일
1

Book

목록 보기
1/1
post-thumbnail

안녕하세요, dvHuni 입니다!!

오늘은 읽었던 책에 대한 정리를 조금 해보려고 해요!!

바로 객체지향의 사실과 오해와 오브젝트를 쓰신 조영호 님께서 번역하신 책!

엘레강트 오브젝트입니다!

책에서는 다양한 객체지향 원칙들을 주장하고 있으며, 그중에는 저자의 생각이 옳다고 생각하시는 부분도 있고 그렇지 않은 부분도 있다고 하셨고,

조영호님께서 쓰신 책에서는 실무 프로그래밍 측면에서의 가이드가 부족하다고 하셨는데, 엘레강트 오브젝트는

그러한 부분을보완해 주기도 한다고도 하셨습니다.

이제야 조금 실무 할줄 안다고 생각했던 제가 객체지향의 원칙을 얼마나 이해하고 있으며

적용하고 있는지 되돌아보고, 공감이 되는 원칙들을 정리함으로써 자신의 것 으로 만들어 보려 합니다.

책을 읽으신 분이라면 어떤 부분을 함께 공감하는지,

책을 읽지 않으신 분이라면 다른 내용은 어떤게 있는지 생각해보시면서 글을 읽으셔도 좋을 것 같습니다!

그러면 출발해보시죠! 😆


1.2 생성자 하나를 주 생성자로 만드세요

객체를 초기화 하는 하나의 주된 생성자를 만들고, 그 외의 방법으로 객체를 초기화 해야 할때는 부 생성자를 만들어 주된 생성자를 활용하도록 하는 원칙입니다.

이는 원칙의 핵심은 중복코드를 방지하고 설계를 더 간결하게 만들어 유지보수성이 향상되는것 입니다.

각각의 부 생성자에 오류검출을 위한 코드를 넣기보단,

주 생성자에 오류검출 코드를 넣게되면 코드의 중복도 방지하고, 간결해 집니다.

1.3 생성자에 코드를 넣지 마세요

리팩터링 시에 생성자에 여러가지 코드가 들어있다면, 리팩터링을 수행하는 프로그래머는 생성자의 코드를 먼저 이해해야됩니다.

생성자는 말그대로 객체를 생성하는 일에 집중하도록 해야하며, 이로인해 생성자의 역할이 간결하고 투명해진다는 것 입니다.

위 원칙을 지킨 객체들에 대해 “이 객체들은 좋은 방식으로 매우 게으릅니다.” 라는 문장이 머리를 한대 치는것 같았습니다.

객체 생성만으로도 원하는 값을 만들어 내도록 여러가지 코드를 생성자에 집어넣었던 저로써는 상당히 충격적이였습니다.

2.3 항상 인터페이스(= 프로토콜)를 사용하세요

책은 자바 언어 기반으로 작성되어 있으며 Swift에서는 인터페이스 = 프로토콜로 생각해주시면 됩니다.

DI와 일맥상통한다고 생각했습니다.

프로토콜을 이용하면 구체적인 구현 내용에 얽매이지 않게 되며 이는 객체간의 결합도를 낮추게됩니다.

객체간의 결합도를 낮추게 되면 하나의 객체의 변화가 다른 객체에 미치는 영향을 줄일 수 있습니다.

이는 유지보수에 있어서 매우 중요한 요소입니다.

2.4 메서드 이름은 신중하게 선택하세요

올바르게 지은 메서드의 이름은 객체를 설계한 목적과 수행임무, 의미를 더 잘 이해할 수 있게 합니다.

여기에는 몇가지 규칙이 있습니다.

2.4.1 값을 리턴하는 builder 메서드는 명사여야 합니다.

객체지향적 사고에서는 객체는 존중받아야 하며, 이는 실 생활에 빗대어 보면 이해 할 만한 예시가 있습니다.

커피집을 예시로 든다면, 커피를 만들어주는 바리스타는 객체가 될 것입니다.

저희는 바리스타에게 “커피 한잔" 을 주문하지, “커피를 어떤 방식을 이용하여 끓여주세요”라고 구체적으로 주문하지 않습니다. (물론 그렇게 주문할 수도 있지만 일반적인 경우를 생각해봅시다)

바리스타에게 커피를 만드는 방법은 위임하고, 저희는 커피를 받을 뿐 입니다.

다시말해 저희는 객체가 어떻게 동작하는지에 대해 관심이 없습니다. 다만 객체가 건네준 정보가 필요할 뿐 입니다.

따라서 메서드의 이름은 어떻게 만들지(how to work)가 아닌, 무엇을 만들지(what to build)를 객체에게 알려주어야 합니다.

2.4.2 엔티티를 수정하는 manipulator 메서드는 동사여야 합니다.

엔티티란 객체에 의해 추상화된 데이터로 이해해주시면 될 것 같습니다.

2.4.1과 마찬가지로 실 생활에서 예를 들어보겠습니다.

정원을 가꾸는 정원사에게 저희는 “정원을 더 화려하게 해주세요” 라고 요청하거나, “정원을 깔끔하게 해주세요" 라는 요청을 할 수 있습니다.

하지만 정원사에게 “정원을 가꾸고 저에게 커피를 가져다 주세요" 라는 요청은 상당히 엉뚱맞으며, 정원사에 대한 배려가 없는 행동입니다.

다시말해 엔티티를 수정하는 메서드의 이름은 무엇을 할지(what to do)에 대한 내용이 되어야 하며,

더욱 중요한것은 무언가를 반환해서는 안된다는 것 입니다.

2.4.3 builder와 manipulator를 조합하기

값을 변화시키면서 리턴해야 하는 경우가 있습니다. 책의 예시를 그대로 사용해보겠습니다.

파일내용을 저장하고 저장된 바이트 수를 반환하는 메서드 입니다.

// Java
class Document {
	int write(InputStream content);
}

저는 애초에 이 메서드의 목적이 불분명하다고 생각합니다. 이미 두가지의 목적이 포함되어 있기 때문이죠.

따라서 목적을 분리하면, 그에 맞게 메서드도 분리될 것 같습니다.

  1. 파일내용 저장 (write) : manipulator
  2. 바이트 수 반환 (byteCount) : builder

책의 내용과 제가 생각한 내용은 여기까지 동일합니다.

그 이후는 실제 코드를 통해 리팩토링 하는 내용이며 Java로 작성되어있습니다.

책의 코드는 제가 리팩토링한 내용과는 조금 다른데, 여러분도 직접 리팩토링을 해보고 어떤 차이가 있는지, 왜 이렇게 했을지 생각해보시면 좋을 것 같습니다.

// Java
class Document {
	OutputPipe output();
}

class OutputPipe {
	void write(InputStream content);
	int bytes();
	long time();
}

2.4.4 boolean을 리턴하는 경우

앞선 원칙에 따르면 값을 리턴하기 때문에 builder 메소드 일 것 이며, 메서드 이름은 명사가 될 것 입니다.

실제 사용하고 있는 예시를 살펴보겠습니다.

String의 isEmpty, Object(NSObject)의 isEqual 등이 있습니다.

boolean 값을 리턴하는 경우는 가독성을 위해 형용사로 지어야 하는 예외의 규칙을 둡니다.

추가적으로 ‘is’라는 접두사는 항상 중복되기 때문에 이를 메서드 이름에 포함시키지 않아야 한다고 합니다.

‘is’접두사를 붙이면 Boolean isEmpty(); 와 같은 코드가 되어 가독성이 안좋아지기 때문입니다.

Java에서는 타입이 앞에 명시되어 문제를 일으키지만, Swift 에서는 크게 문제가 될 것 같진 않습니다.

func isEmpty() → Bool

형용사로 지어야 하는 규칙은 공감을 하지만, Swift 환경에서는 is 접두사를 사용하여도 무방할 것 같습니다.

2.5 퍼블릭 상수를 사용하지 마세요

상수를 사용하는 이유는 객체간에 데이터를 공유하기 위해서 입니다.

객체들은 서로 독립적이여야 하며, 닫혀 있어야 합니다.

상수를 이용해서 객체간에 데이터를 공유하는것은 캡슐화와 객체지향적 사고 전체를 부정하는 일 입니다.

많은 코드를 본것은 아니지만 자주 사용되는 방법중에 하나는 다음과 같은 코드였습니다.

enum Const {
	static let size: CGSize = CGSize(width: 100, height: 100)
	static let color: UIColor = .green
}

위 코드는 두가지의 문제를 발생시킵니다.

2.5.1 결합도 증가

앞서 적었던 코드를 활용한 코드를 작성해보겠습니다.

final class Brush {
    func coloring(_ view: inout UIView) {
        view.backgroundColor = Const.color
    }
}

final class Pencil {
    func tinting(_ view: inout UIView) {
        view.tintColor = Const.color
    }
}

이제 Const enum의 color가 변경되면, BrushPencil의 각 메소드는 원하지 않는 결과를 만들어냅니다.

지금은 한 파일 안에서 작성 되어있고 사용되는 부분이 적어 변화를 눈치채기 쉽지만,

많은 파일들과 클래스가 존재한다면, 사용되는 모든 부분에서 변화에 대한 확인과 대응이 이루어져야 합니다.

이처럼 여러 객체가 하나의 상수에 의해 결합되어지기 때문에 결합도가 증가합니다.

2.5.2 응집도 감소

응집도에 대한 사전적 의미는 모듈 내부의 기능적인 응집 정도이지만,
책에서 언급된 응집도는 “객체가 문제를 해결하는데 얼마나 집중되어 있는가” 라는 의미로 다가왔습니다.

그런 측면에서 위에서 작성된 코드는 응집도가 낮습니다. coloringtinting이라는 문제를 해결하기 위해 외부의 내용(Const)를 가져다 사용했으니까요.

그렇다면 응집도가 낮은 코드는 어떤 문제가 생길까요?

여러가지 문제가 생길 수 있겠지만 책에서 가장 중요하게 생각하는 유지보수의 관점에서 생각되는 문제점들만 몇가지 적어보겠습니다.

먼저 외부의 변화에 의해 결과가 달라져 의도치 않은 결과를 얻을 수 있습니다.

앞서 예시를 보았듯이, Const의 변화에 의해 의도와는 다른 결과를 얻을 수 있으며, 이 원인을 찾는것은 매우 험난한 일이 될 수 도 있습니다.

재사용이 어렵습니다.

객체 내부의 문제를 해결하는데 외부의 내용을 가져다 사용해야 하기 때문에 원치않는 내용들도 포함되기 마련입니다.

그렇게되면 코드는 재사용하기 어렵고, 중복된 코드가 많아질 수 밖에 없습니다.

낮은 응집도를 해결하기 위해 책에서 제시한 방법은 “새로운 기능 클래스를 추가하여 해당 기능을 분리하는 것” 입니다.

2.7 문서를 작성하는 대신, 테스트 코드를 작성하세요

최근 TDD를 공부하면서, 또 프로덕트에서 테스트 코드를 작성하면서 느낀점과 일맥 상통하여 상당히 공감 갔던 부분입니다.

테스트는 코드의 동작을 나타내는 가장 좋은 문서라고 생각되었습니다.

몇줄의 주석보다 테스트를 통해 어떤 내용을 검증하고, 어떤 동작을 기대하는지 자세하게 표현해 낼 수 있습니다.


마무리하며

공감되거나, 깊이 와닿은 내용을 하나의 글로 정리하고 싶었지만, 너무 많은 내용이 담기게 될 것 같아 내용을 분리합니다.

2장 까지 읽고 나서의 느낀점은..

내용은 너무 좋은데.. 프로덕트 레벨에서 적용할 수 있을까? 라는 의문이였습니다.

많은 개발자가 협업하게 되는 프로덕트 레벨에서는, 모두의 공감이 있어야 위 내용들을 잘 준수할 수 있을 것 같았습니다.

(method 네이밍 이라던가..)

그래서 협업을 하면서도 지킬 수 있을 만한 내용(1.3)은 조금씩 실천해 보려고 합니다.

그게 유지보수성을 좋게 하는 길이니까..!

엘레강트 오브젝트 #2에서 이어집니다.. 🏃‍♂️

profile
hello, iOS

0개의 댓글