독서

책01. 파이브라인즈 오브 코드 06. 데이터보호

제비랑 2024. 3. 3. 11:40

파이브 라인스 오브 코드 - 크리스찬 클라우젠

6장. 데이터 보호

6.1 getter없이 캡슐화하기

getter와 setter를 사용하지 말것.

  • getter와 setter는 결국 캡슐화된 객체의 필드의 캡슈로하를 해제하고, 불변속성을 전역적으로 만든다.
  • 가변객체에서 발생하는 문제라서, bool 타입을 예외로한다.
    말이 너무 어려워서, 곱씹을 수 있도록 전문을 쓴다.
  • 여기서 setter 또는 getter를 언급할 때는 각각 부울이 아닌 필드를 직접 할당하거나 반환하는 메서드를 의미합니다. C# 프로그래머의 경우 이 정의에 프로퍼티(property)도 포함됩니다. 메서드의 이름과는 아무런 상관이 없습니다. getX라고 부를수도 있고 아닐수 도 있습니다. getter와 setter는 흔히 private 필드를 다루기 위한 메서드로 캡슐화와 함께 배웁니다. 그러나 객체의 필드에 대한 getter가 존재하는 순간 캡슐화를 해제하고 불변속성을 전역적으로 만들게 됩니다. 객체를 반환한 후 이를 반환받은 곳에는 이 객체를 더 많은 곳에 전달할 수 있으며, 이는 우리가 제어할 수 없습니다. 객체를 얻은 어느 곳에서나 public 메서드를 호출할 수 있으며 아마도 예상하지 못한 방식으로 객체를 수정할 수 있습니다.

setter도 비슷한 문제를 제기합니다. 이론적으로 setter는 내부 데이터 구조를 변경하고 해당 setter를 수정해도 *시그니처(signature)를 유지할 수 있는 또 다른 간접적인 레이어를 도입할 수 있습니다. 이 규칙의 정의에 따르면 메서드는 더이상 setter가 아니므로 문제가 되지 않습니다. 그러나 실제로 일어나는 일은, setter를 통한 새로운 데이터 구조를 반환하고 getter를 수정하는 것입니다. 그런 다음 수신자 측에서 이 새로운 데이터 구조를 받을 수 있도록 수정해야합니다. 이것은 정확히 우리가 피하고 싶어하는 밀 결합(tight couping)의 형태입니다.

이런 문제는 가변(mutable) 객체에서만 발생하는 문제입니다. 그러나 이 규칙은 불변(immutable)필드에도 비공개 필드를 적용해서 얻는 또 다른 효과, 즉 제안하는 아키텍처 때문에 부울(Boolean)만을 예외로 하고 있습니다. 필드를 비공개로 하는 것의 가장 큰 장점은 그렇게 하는 것이 푸시 기반(push-based)의 아키텍처를 장려하기 때문입니다. 푸시 기반 아키텍처에서는 가능한 한 데이터에 가깝게 연산을 이관하지만, 풀 기반(pull based)의 아키텍처에서는 데이터를 가져와 중앙에서 연산을 수행합니다.

시그니처 : 함수와 메서드의 이름을 포함한 입력과 출력의 정의부분

  • 풀 기반 아키텍처 : 기능을 수행하는 메서드 없이 수많은 '수동적인' 데이터 클래스와 여기저기서 데이터를 혼합해서 모든 작업을 수행하는 소수의 '관리자'클래스로 이어집니다. 이 접근 방식은 데이터와 관리자사이, 그리고 암묵적으로 데이터 클래스간에도 밀 결합을 가져옵니다.
//풀기반 아키텍처
class Website{ constructor(private url : string){ }
    get Url() {
    return this.url;
    }
}
class User { 
constructor(private username : string){ }
  getUsername() {
    return this.username; }
}
class BlogPost{
  constructor(private author : User, private id : string) { }
  getId() { return this.id;}
  getAuthor() { return this.author;}
}
function generatedPostLink(website : Webstie, post: BlogPost){
  let url = website.getUrl(); let user = post.getAuthor();
  let name = user.getUsername();
  let postId = post.getId();
  return url + name + postId;
}
  • 푸시 기반 아키텍처 : 데이터를 가져오는 대신, 인자로 데이터를 전달합니다. 결과적으로 모든 클래스가 자신의 기능을 가지고 있으며 코드는 그 효용에 따라 분산.
//푸시 기반 아키텍처
class Website{
  constructor(private url : string){ }
  generateLink(name : string, id : string){
    return this.url + name + id;
  }
}
class User {
  constructor(private username : string){ }
  generateLink(webSite : Website, id : string){
    return website.generateLink(this.username, id);
  }
}  
class BlogPost{  
    constructor(private author : User, private id : string) { }  
    generateLink(website : Website) {  
    return this.author.generateLink(website, this.id);
}  
function generatedPostLink(website : Website, post: BlogPost){  
    return post.generateLink(website);  
}

뭔가 푸시기반은 내부적으로 필드를 직접 반환해서 조립하는게 아니라, 부품을 인자로서, 조립까지 맡기는 느낌.
다나와 견적맞추고 조립 맡기는것과, 부품 받아서 조립하기 차이인듯.

관련 스멜 : 디미터 법칙

낯선사람에게 말하지 말라

  • 낯선사람 : 우리가 직접 접근할 수는 없지만, 참조를 얻을 수 있는 객체

리팩터링 패턴 : getter와 setter 제거하기

1, getter 또는 setter가 사용되는 모든 곳에서 오류가 발생하도록 비공개로 설정한다.(private 추가)
2. 클래스로의 코드이관 리팩터링 패턴으로 오류를 수정한다.
3. getter 또는 setter는 클래스로의 코드 이관의 일부로 인라인화 됩니다. 따라서 사용하지 않으므로 삭제해서 다른 사람이 사용하지 않게 합니다.

6.2 간단한 데이터 캡슐화하기

공통 접사를 사용하지 말아라!

  • 코드에는 공통 접사가 있는 메서드나 변수가 없어야한다.
    ex) accountDeposit(), accountTransfer()과 같이 함수를 만들지말고, Account 클래스를 정의한뒤 메서드로 Deposit()과 Transfer()를 만든다.

관련 스멜 : 단일 책임 원칙 (여기서 Solid가?)

클래스에는 단 하나의 책임만 있어야 하는다.

리팩터링패턴 : 데이터 캡슐화

  1. 클래스를 만든다.
  2. 변수를 새로운 클래스로 이동하고 let을 private으로 바꿉니다. 변수의 이름을 단순한 것으로 정하고 변수에 대한 getter와 setter를 만듭니다.
  3. 변수가 더 이상 전역 범위에 없기 때문에 컴파일러가 오류를 발생시킨다. 다음 절차로 오류를 수정한다.
    ㄱ. 새 클래스의 인스턴스에 적합한 변수 이름을 선택.
    ㄴ. 접근을 가상의 변수에 대한 getter 또는 setter로 바꾼다.
    ㄷ. 2개 이상의 다른메서드에서 오류가 발생한 경우 이전의 변수명을 가진 매개변수를 첫 번째 매개변수로 추가하고 동일한 변수를 첫번째 인자 로 호출하는 쪽에 넣는다.
    ㄹ. 한 메서드에서만 오류가 날 때까지 반복한다.
    ㅁ. 변수를 캡슐화했다면 변수가 선언된 지점에서 새로운 클래스를 인스턴스화합니다.6.3 복잡한 데이터 캡슐화
  • 데이터 캡슐화, 클래스로의 코드이관, 메서드의 인라인화, getter와 setter 제거하기 등을 사용한다.

6.4 순서에 존재하는 불변속성 제거하기

  • 먼저 호출해야하는것을 생성자로 만들어, 순서를 강제한다.

리팩터링패턴 : 순서 강제화.

  1. 마지막으로 실행되어야 하는 메서드에 데이터 캡슐화를 적용합니다.
  2. 생성자가 첫 번째 메서드를 호출하도록 합니다.
  3. 두 메서드의 인자가 연결되어 있으면 이러한 인자를 필드로 만들고 메서드에서 제거합니다.

6.5 열거형을 제거하는 또다른 방법(클래스로의 코드 이관 외)

  • 기존에 열거형을 제거하는 방법 : 인터페이스를 만들고, 인터페이스의 구현체를 만든다.
  • 새로운 방법 : 비공개 생성자를 가진 클래스를 사용하는 것.

요약

  • 캡슐화를 시행하려면 데이터 노출을 피해야 한다.
  • getter와 setter 를 사용하지 말것 : 간접적으로 비공개 필드를 노출하지 말것.
  • 공통 접사를 사용하지 말것 : 공통접두/접미사가 있는 메서드와 변수가 있을 경우, 한 클래스에 있어야 한다.
  • 클래스를 이용한 순서 강제화 리팩터링 패턴을 사용하면 컴파일러가 실행순서를 강제하고, 순서 불변속성이 제거된다.
  • 열거형을 제거하는 또다른 방법은, 비공개 생성자를 가진 클래스를 사용하는 것이다.

책의 1부를 쭉 훑어보았다.
블로그에서 요약한 내용을 보면서 다시금 어떤 리팩터링 패턴이 있었는지 리마인드해야겠다.
추후 2부를 요약할 날이 있었으면 좋겠따..*