"다양한 내용을 캡슐화"라고 할 때 무엇을 의미합니까?


25

내가 접한 OOP 원칙 중 하나는 다음과 같습니다.

나는 문구의 문자 적 ​​의미가 무엇인지 이해합니다. 그러나 더 나은 디자인에 어떻게 기여하는지는 잘 모르겠습니다. 누군가 좋은 예를 사용하여 설명 할 수 있습니까?


잘 설명하는 en.wikipedia.org/wiki/Encapsulation_(computer_programming) 를 참조하십시오 . 때로는 상수를 캡슐화해야하기 때문에 "다양한 것"이 올바르지 않다고 생각합니다.
qwerty_so

I don't know how exactly would it contribute to a better design캡슐화 세부 사항은 "모델"과 구현 세부 사항 사이의 느슨한 결합에 관한 것입니다. 구현 세부 사항에 대한 "모델"이 적을수록 솔루션이 더 유연합니다. 그리고 진화하기가 더 쉽습니다. "세부 사항에서 자신을 요약하십시오".
Laiv

@Laiv 따라서 "변형"은 소프트웨어 수명주기 동안 무엇이 진화하거나 프로그램 실행 중에 어떤 변화가 발생합니까?
Haris Ghauri

2
@HarisGhauri 둘 다. 다양한 것을 함께 그룹화하십시오. 독립적으로 변하는 것을 분리하십시오. 결코 변하지 않을 것이라고 생각하는 것을 의심하십시오.
candied_orange

1
@laiv 생각 "추상"은 좋은 지적입니다. 그렇게하는 것이 압도적 일 수 있습니다. 어느 하나의 대상에서 하나의 책임을 져야합니다. 그것에 대한 좋은 점은 여기서 한 가지만 신중하게 생각해야한다는 것입니다. 문제의 나머지 부분에 대한 세부 사항이 다른 사람의 문제 일 때 일이 쉬워집니다.
candied_orange

답변:


30

다음과 같은 코드를 작성할 수 있습니다.

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

또는 다음과 같은 코드를 작성할 수 있습니다.

pet.speak();

다양한 것이 캡슐화되어 있으면 걱정할 필요가 없습니다. 당신은 당신이 필요로하는 것과 당신이 무엇을 사용하고 있는지에 대해 걱정할 것입니다.

다양한 것을 캡슐화하고 다양한 것을 신경 쓰는 코드를 확산시킬 필요가 없습니다. 애완 동물을 그 유형으로 말하는 방법을 알고있는 특정 유형으로 설정 한 후에는 어떤 유형을 잊어 버리고 애완 동물처럼 취급 할 수 있습니다. 어떤 유형을 요구하지 않아도됩니다.

getter가 액세스해야하므로 type이 캡슐화되었다고 생각할 수 있습니다. 난 아니야 게터는 실제로 캡슐화하지 않습니다. 그들은 누군가가 당신의 캡슐화를 깨뜨릴 때 진정합니다. 그것들은 디버깅 코드로 가장 많이 사용되는 가로 방향 후크와 같은 멋진 데코레이터입니다. 어떻게 얇게 썰어도 여전히 유형이 노출됩니다.

이 예제를보고 다형성과 캡슐화를 혼동한다고 생각할 수 있습니다. 난 아니에요. 나는 "변하는 것"과 "세부 사항"을 혼동하고 있습니다.

당신의 애완 동물이 개라는 사실은 세부 사항입니다. 당신을 위해 다양 할 수 있습니다. 그렇지 않을 수도 있습니다. 그러나 확실히 사람마다 다를 수 있습니다. 이 소프트웨어가 개 애호가 만 사용할 것이라고 생각하지 않으면 개를 세부 사항으로 취급하고 캡슐화하는 것이 현명합니다. 그렇게하면 시스템의 일부가 개를 행복하게 인식하지 못하고“앵무새가 우리”와 합쳐질 때 영향을받지 않습니다.

나머지 코드에서 세부 정보를 분리, 분리 및 숨 깁니다. 세부 사항에 대한 지식을 시스템에 퍼 뜨리지 말고 "다양한 내용을 캡슐화"하십시오.


3
정말 이상합니다. "다양한 내용을 캡슐화"한다는 것은 전역 변수가없는 상태 변화를 숨기는 것을 의미합니다. 그러나 캡슐화보다 다형성에 대한 답이 더 많다고해도 대답은 의미가 있습니다. :
David Arno

2
@DavidArno 다형성은이 작업을 수행하는 한 가지 방법입니다. 애완 동물로 if 구조를 크 래밍 할 수 있었고 애완 동물의 캡슐화 덕분에 여기가 멋지게 보일 것입니다. 그러나 그것은 엉망을 청소하는 대신 엉망으로 움직일 것입니다.
candied_orange

1
"다양한 내용을 캡슐화"한다는 것은 상태 변화를 숨기는 것을 의미합니다 . 아냐 나는 CO의 의견을 좋아한다. Derick Elkin의 답변 은 더 깊어졌습니다. 두 번 이상 읽으십시오. @JacquesB가 말했듯이 "이 원칙은 실제로 매우 깊다"
radarbob

16

여기서 "가변"은 "요구 사항 변경으로 인해 시간이 지남에 따라 변경 될 수 있음"을 의미합니다. 이것이 핵심 설계 원칙입니다. 향후 개별적으로 변경해야 할 코드 또는 데이터를 분리하고 분리합니다. 단일 요구 사항이 변경되면 이상적으로 는 관련 코드를 한 곳에서 변경해야합니다. 그러나 코드 기반이 잘못 설계되어 (예 : 상호 연결성이 높고 요구 사항에 대한 논리가 여러 곳에 퍼져있는 경우) 변경이 어려워 예기치 않은 결과를 초래할 위험이 높습니다.

많은 장소에서 판매 세 계산을 사용하는 응용 프로그램이 있다고 가정하십시오. 판매 세율이 변경되면 다음 중 원하는 것을 선택하십시오.

  • 판매 세율은 판매 세가 계산되는 응용 프로그램의 모든 곳에 하드 코딩 된 리터럴입니다.

  • 판매 세율은 글로벌 상수이며, 판매 세가 계산되는 응용 프로그램의 모든 곳에서 사용됩니다.

  • calculateSalesTax(product)판매 세율이 사용되는 유일한 방법이라고하는 단일 방법 이 있습니다.

  • 판매 세율은 구성 파일 또는 데이터베이스 필드에 지정됩니다.

판매 세율은 다른 요구 사항과 무관 한 정치적 결정으로 인해 변경 될 수 있으므로 구성에서 분리하는 것을 선호하므로 코드에 영향을주지 않고 변경할 수 있습니다. 그러나 판매 세 계산 논리가 변경 될 수도 있습니다 (예 : 제품마다 다른 요율). 따라서 계산 논리도 캡슐화하고 싶습니다. 글로벌 상수는 좋은 생각처럼 보이지만 실제로는 한 곳이 아닌 프로그램에서 다른 곳에서 판매 세를 사용하도록 장려 할 수 있으므로 실제로 나쁩니다.

이제 코드의 많은 곳에서 사용되는 또 다른 상수 Pi를 고려하십시오. 동일한 설계 원칙이 적용됩니까? 아니요, Pi는 변경되지 않기 때문입니다. 구성 파일이나 데이터베이스 필드로 추출하면 불필요하게 복잡해집니다 (그리고 다른 모든 것들은 가장 간단한 코드를 선호합니다). 불일치를 피하고 가독성을 높이기 위해 여러 곳에서 하드 코딩하는 대신 전역 상수로 만드는 것이 합리적입니다.

요점은, 우리가 지금 프로그램이 어떻게 작동하는지 살펴보면 판매 세율과 Pi는 동등하며 둘 다 상수입니다. 우리 가 미래에 변할 수 있는 것을 고려할 때에 만 디자인에서 그것들을 다르게 취급해야한다는 것을 알고 있습니까?

이 원칙은 실제로 코드 기반이 오늘날 해야 할 일을 넘어서야 하고 변경을 야기 할 수있는 외부 세력을 고려해야하며 요구 사항의 다양한 이해 관계자를 이해해야 하기 때문에 실제로는 매우 깊 습니다.


2
세금은 좋은 예입니다. 법과 세금 계산은 하루마다 바뀔 수 있습니다. 세금 신고 시스템을 구현하는 경우 이러한 종류의 변경과 밀접한 관련이 있습니다. 또한 한 로케일에서 다른 로케일 (국가, 지방 등)로 변경
Laiv

"Pi는 변하지 않을 것"이라고 웃었다. 사실 Pi는 변하지 않을 것입니다. 그러나 더 이상 사용할 수 없다고 가정 해보십시오. 일부 사람들이 Pi를 사용하지 않으면 더 이상 사용되지 않습니다. 그것이 요구 사항이된다고 가정 해보십시오. 당신은이 희망 행복 타우 일 . 좋은 답변 BTW. 정말로 깊다.
candied_orange

14

현재의 답변은 모두 부분적으로 만 히트 한 것으로 보이며 핵심 아이디어를 흐리게하는 예제에 중점을 둡니다. 이것은 (전적으로) OOP 원칙이 아니라 일반적으로 소프트웨어 설계 원칙입니다.

이 문구에서 "가변적"인 것은 코드입니다. 크리스토프는 것이 보통이라고 말하는 점에 있습니다 당신이 자주 변화, 예상 이. 목표는 코드의 향후 변경으로부터 자신을 보호하는 것입니다. 이것은 인터페이스에 대한 프로그래밍과 밀접한 관련이 있습니다 . 그러나 Christophe는이를 "구현 세부 사항"으로 제한하지 않습니다. 실제로이 조언의 가치는 종종 요구 사항의 변화로 인해 발생합니다 .

이것은 David Arno가 생각하는 캡슐화 상태와 간접적으로 만 관련이 있습니다. 이 조언이 항상 캡슐화 상태를 제안하는 것은 아니며 종종 불변의 객체에도 적용됩니다. 실제로 상수의 이름을 지정하는 것은 다양한 것을 캡슐화하는 (매우 기본적인) 형태입니다.

CandiedOrange는 "세부 사항"으로 "다양한 내용"을 명시 적으로 나타냅니다. 이것은 부분적으로 만 정확합니다. 나는 다양한 코드가 어떤 의미에서는 "세부 사항"이지만 "세부 사항"을 정의하지 않는 한 "세부 사항"은 변하지 않을 수 있다는 데 동의합니다. 다양하지 않은 세부 사항을 캡슐화해야하는 이유가있을 수 있지만이 내용은 하나가 아닙니다. 대략적으로, "dog", "cat"및 "duck"이 처리해야 할 유일한 유형이라는 확신이 있다면 CandiedOrange가 리팩토링을 제안하지 않습니다.

다른 상황에서 CandiedOrange의 예제를 캐스팅하는 경우 C와 같은 절차 언어가 있다고 가정합니다.

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

앞으로이 코드 조각이 변경 될 것이라고 합리적으로 기대할 수 있습니다. 새로운 프로 시저를 정의하여 간단히 "캡슐화"할 수 있습니다.

void speak(pet) {
  if (pet.type() == dog) {
    pet.bark();
  } else if (pet.type() == cat) {
    pet.meow();
  } else if (pet.type() == duck) {
    pet.quack()
  }
}

코드 블록 대신이 새로운 프로 시저를 사용합니다 (예 : "추출 방법"리팩토링). 이 시점에서 "cow"유형을 추가하거나 speak절차를 업데이트하기 만하면됩니다. 물론 OO 언어에서는 CandiedOrange의 답변에서 언급 한 것처럼 동적 디스패치를 ​​활용할 수 있습니다. pet인터페이스를 통해 액세스하면 자연스럽게 발생합니다 . 동적 디스패치를 ​​통해 조건부 논리를 제거하는 것은 내가이 절차 적 표현을 한 이유 중 하나 인 직교 관심사입니다. 또한 OOP에만 해당되는 기능이 필요하지 않다는 점을 강조하고 싶습니다. OO 언어에서도 다양한 내용을 캡슐화한다고해서 반드시 새로운 클래스 나 인터페이스를 만들어야하는 것은 아닙니다.

좀 더 OO에 가깝지 않은 좀 더 전형적인 예로써, 목록에서 중복을 제거하고 싶다고 가정 해보십시오. 다른 목록에서 지금까지 본 항목을 추적하고 본 항목을 제거하여 목록을 반복하여 구현한다고 가정 해 봅시다. 적어도 성능상의 이유로 보이는 항목을 추적하는 방법을 변경하려고 할 수 있다고 가정하는 것이 합리적입니다. 다양한 것을 캡슐화하는 규범은 보이는 항목 세트를 나타내는 추상 데이터 유형을 작성해야 함을 나타냅니다. 우리의 알고리즘은 이제이 추상 집합 데이터 형식에 대해 정의되며, 이진 검색 트리로 전환하기로 결정하면 알고리즘을 변경하거나 관리 할 필요가 없습니다. OO 언어에서는 클래스 또는 인터페이스를 사용하여이 추상 데이터 유형을 캡처 할 수 있습니다. SML / O '와 같은 언어로

요구 사항 중심의 예에서는 일부 비즈니스 논리와 관련하여 일부 필드의 유효성을 검사해야한다고 가정하십시오. 현재 특정 요구 사항이있을 수 있지만 요구 사항이 진화 할 것이라고 강력하게 의심합니다. 현재 로직을 자체 프로 시저 / 함수 / 규칙 / 클래스에 캡슐화 할 수 있습니다.

이것이 "다양한 것을 캡슐화하는"의 일부가 아닌 직교 관심사이지만, 현재 캡슐화 된 논리에 의해 매개 변수화되는 것은 당연하다. 이는 일반적으로보다 유연한 코드로 이어지고 캡슐화 된 논리를 수정하는 대신 대체 구현으로 대체하여 논리를 변경할 수 있습니다.


씁쓸하고 달콤한 아이러니. 예, 이것은 전적으로 OOP 문제가 아닙니다. 당신은 언어 패러다임의 세부 사항이 내 대답을 오염시키는 것을 포착하고 패러다임을 "변함"으로써 나에게 올바르게 처벌했습니다.
candied_orange

"심지어 OO 언어로, 어떤 변화를 캡슐화하는 것은 반드시 생성 할 수있는 새로운 클래스 또는 인터페이스 요구 사항을 의미하지 않는다"- 그것은 SRP 위반하지하는 새로운 클래스 나 인터페이스를 생성하지 않는 상황을 상상하기 어렵다
taurelas을

11

"다양한 것 캡슐화"는 변화하고 진화 할 수있는 구현 세부 사항을 숨기는 것을 말합니다.

예:

예를 들어 클래스 가 register () Course를 추적 Students할 수 있다고 가정 합니다 . LinkedList컨테이너를 구현 하고 컨테이너를 노출하여 반복 할 수 있습니다.

class Course { 
    public LinkedList<Student> Atendees; 
    public bool register (Student s);  
    ...
}

그러나 이것은 좋은 생각이 아닙니다.

  • 첫째, 사람들은 register () 메소드를 거치지 않고 학생들이 직접 목록에 추가 할 수 있도록 좋은 행동이 부족하고 셀프 서비스로 사용할 수 있습니다.
  • 그러나 더욱 성가신 것은 사용 된 클래스의 내부 구현 세부 사항에 "코드 사용"의 의존성을 만듭니다. 예를 들어 배열, 벡터, 좌석 번호가있는지도 또는 고유 한 영구 데이터 구조를 사용하려는 경우 클래스의 향후 발전을 방해 할 수 있습니다.

다양한 것을 캡슐화하면 (혹은 말한대로 달라질 수 있음), 사용하는 코드와 캡슐화 된 클래스 모두가 서로를 자유롭게 진화시킬 수있는 자유를 유지합니다. 이것이 OOP에서 중요한 원칙입니다.

추가 자료 :

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.