왜 수업을 “공개”하도록 설계해서는 안됩니까? 질문과 다른

다양한 스택 오버플로 질문과 다른 사람의 코드를 읽을 때 클래스 디자인 방법에 대한 일반적인 합의가 마감됩니다. 이것은 기본적으로 Java 및 C #에서 모든 것이 비공개이며, 필드는 최종적이며 일부 메소드는 최종적이며 때로는 클래스도 최종적 입니다.

그 뒤에 아이디어는 구현 세부 사항을 숨기는 것인데, 이는 매우 좋은 이유입니다. 그러나 protected대부분의 OOP 언어와 다형성이 있기 때문에 작동하지 않습니다.

수업에 기능을 추가하거나 변경할 때마다 나는 보통 사방에 배치되어 있고 사적으로 배치되어 있습니다. 여기에서 구현 세부 사항이 중요합니다. 구현을 수행하고 확장하여 결과가 무엇인지 완전히 알고 있습니다. 그러나 개인 및 최종 필드 및 메소드에 액세스 할 수 없으므로 세 가지 옵션이 있습니다.

  • 클래스를 확장하지 말고 문제를 해결하여 더 복잡한 코드로 연결하십시오.
  • 전체 클래스를 복사하여 붙여 넣어 코드 재사용 성
  • 프로젝트를 포크

그것들은 좋은 옵션이 아닙니다. protected지원 언어로 작성된 프로젝트에서 왜 사용 되지 않습니까? 일부 프로젝트가 클래스에서 상속을 명시 적으로 금지하는 이유는 무엇입니까?



답변

확장 할 때, 특히 확장을 수행하는 프로그래머가 클래스의 작동 방식을 완전히 이해하지 못하는 경우 클래스가 올바르게 작동하도록 설계하려면 상당한 노력이 필요합니다. 개인 정보를 모두 공개하여 공개하거나 보호 할 수는 없습니다. 다른 사람이 변수의 값을 변경할 수있게하려면 가능한 모든 값이 클래스에 어떤 영향을 미치는지 고려해야합니다. (변수를 null로 설정하면 어떻게됩니까? 빈 배열? 음수?) 다른 사람이 메소드를 호출 할 수있게하는 경우에도 마찬가지입니다. 신중한 생각이 필요합니다.

따라서 수업을 열면 안되는 것은 아니지만 때로는 수업을 여는 데 가치가 없습니다.

물론, 도서관 저자들이 게으른 것일 수도 있습니다. 당신이 말하는 라이브러리에 따라 다릅니다. 🙂


답변

기본적으로 모든 것을 비공개로 만드는 것은 거칠게 들리지만 반대쪽에서 살펴보십시오. 기본적으로 모든 것이 비공개 인 경우 무언가를 공개 (또는 거의 같은 것)로 만드는 것은 의식적인 선택입니다. 수업 사용 방법에 대한 귀하와 소비자와의 수업 저자 계약입니다. 인터페이스는 변경되지 않는 한 저자는 클래스의 내부 작업을 자유롭게 수정할 수 있습니다. 수업의 어느 부분을 신뢰할 수 있고 어떤 부분이 변경 될 수 있는지 정확하게 알고 있습니다.

기본 개념은 ‘느슨한 커플 링'( ‘좁은 인터페이스’라고도 함)입니다. 그 가치는 복잡성을 낮추는 데 있습니다. 구성 요소가 상호 작용할 수있는 방법의 수를 줄임으로써 구성 요소 간의 상호 의존도도 줄어 듭니다. 상호 의존성은 유지 관리 및 변경 관리와 관련하여 최악의 복잡한 문제 중 하나입니다.

잘 설계된 라이브러리에서 상속을 통해 확장 할 가치가있는 클래스는 적절한 장소에서 보호 및 공개 멤버를 보호하고 다른 모든 것을 숨 깁니다.


답변

비공개 가 아닌 모든 것은 클래스의 모든 향후 버전에서 변경되지 않은 동작 으로 존재 해야합니다 . 실제로, 문서화 여부에 관계없이 API의 일부로 간주 될 수 있습니다. 따라서 세부 정보를 너무 많이 노출하면 나중에 호환성 문제가 발생할 수 있습니다.

“최종”담당자에 대하여. “밀봉 된”클래스, 일반적인 경우는 불변 클래스입니다. 프레임 워크의 많은 부분은 변경 불가능한 문자열에 의존합니다. String 클래스가 최종 클래스가 아닌 경우, 변경 불가능한 String 클래스 대신 사용되는 경우 다른 많은 클래스에서 모든 종류의 버그를 유발하는 변경 가능한 String 서브 클래스를 쉽게 만들 수 있습니다.


답변

OO에는 기존 코드에 기능을 추가하는 두 가지 방법이 있습니다.

첫 번째는 상속에 의한 것입니다. 당신은 수업을 듣고 그것을 파생시킵니다. 그러나 상속은주의해서 사용해야합니다. 기본과 파생 클래스 사이에 isA 관계 가있을 때 주로 공용 상속을 사용해야합니다 (예 : Rectangle Shape). 대신 기존 구현을 재사용하기 위해 공용 상속을 피해야합니다. 기본적으로 공용 상속은 파생 클래스가 기본 클래스 (또는 더 큰 클래스)와 동일한 인터페이스를 갖도록하기 위해 사용되므로 Liskov의 대체 원칙을 적용 할 수 있습니다 .

기능을 추가하는 다른 방법은 위임 또는 구성을 사용하는 것입니다. 기존 클래스를 내부 객체로 사용하고 구현 작업의 일부를이 클래스에 위임하는 자체 클래스를 작성합니다.

사용하려는 라이브러리의 모든 결승전의 아이디어가 무엇인지 잘 모르겠습니다. 아마도 프로그래머는 코드를 확장하는 가장 좋은 방법이 아니기 때문에 클래스에서 새 클래스를 상속받지 않기를 원했을 것입니다.


답변

대부분의 대답은 옳습니다 : 객체가 설계되거나 확장되지 않을 때 클래스는 봉인되어야합니다 (최종, NotOverridable 등).

그러나 적절한 코드 디자인을 모두 닫는 것을 고려하지는 않습니다. SOLID의 “O”는 “개방형 원리”를위한 것으로 클래스는 수정하려면 “폐쇄”해야하지만 확장에는 “개방”되어야합니다. 아이디어는 객체에 코드가 있다는 것입니다. 잘 작동합니다. 기능을 추가하려면 해당 코드를 열고 이전에 작동하는 동작을 방해 할 수있는 외과 적 변경을하지 않아도됩니다. 대신, 상속 또는 의존성 주입을 사용하여 클래스를 “확장”하여 추가 작업을 수행 할 수 있어야합니다.

예를 들어, “ConsoleWriter”클래스는 텍스트를 가져 와서 콘솔에 출력 할 수 있습니다. 이것은 잘합니다. 그러나 어떤 경우에는 파일에 기록 된 것과 동일한 출력이 필요합니다. ConsoleWriter의 코드를 열고 외부 인터페이스를 변경하여 매개 변수 “WriteToFile”을 기본 기능에 추가하고 추가 파일 작성 코드를 콘솔 작성 코드 옆에 배치하는 것은 일반적으로 나쁜 것으로 간주됩니다.

대신 두 가지 중 하나를 수행 할 수 있습니다. ConsoleWriter에서 파생하여 ConsoleAndFileWriter를 형성하고 Write () 메서드를 확장하여 먼저 기본 구현을 호출 한 다음 파일에 쓸 수도 있습니다. 또는 ConsoleWriter에서 인터페이스 IWriter를 추출하고 해당 인터페이스를 다시 구현하여 두 개의 새 클래스를 작성할 수 있습니다. FileWriter 및 “MultiWriter”는 다른 IWriter에 제공 될 수 있으며 해당 메소드에 대한 모든 호출을 지정된 모든 작성자에게 “브로드 캐스트”합니다. 어떤 것을 선택해야하는지에 따라 달라집니다. 간단한 파생은 훨씬 간단하지만, 결국 네트워크 클라이언트, 세 개의 파일, 두 개의 명명 된 파이프 및 콘솔에 메시지를 보내야한다는 어둡고 통찰력이 있다면 계속 진행하여 인터페이스를 추출하고 문제를 해결하십시오. “Y- 어댑터”; 그것’

이제 Write () 함수가 가상으로 선언되지 않았거나 봉인 된 경우 소스 코드를 제어하지 않으면 문제가 발생할 수 있습니다. 때로는 그렇더라도. 이것은 일반적으로 좋지 않은 입장이며 폐쇄 소스 또는 제한 소스 API 사용자에게는 아무런 문제가 없습니다. 그러나 Write () 또는 전체 클래스 ConsoleWriter가 봉인 된 정당한 이유가 있습니다. 보호 된 필드에는 민감한 정보가있을 수 있습니다 (이 정보를 제공하기 위해 기본 클래스에서 차례로 재정의 됨). 유효성 검사가 충분하지 않을 수 있습니다. API를 작성할 때는 코드를 소비하는 프로그래머가 최종 시스템의 평균 “최종 사용자”보다 더 똑똑하거나 자비 롭지 않다고 가정해야합니다. 그렇게 당신은 결코 실망하지 않습니다.


답변

나는 아마도 모든 사람들이 c 프로그래밍에 oo 언어를 사용하고 있다고 생각합니다. 당신을보고 있어요, 자바 망치 모양이 물체처럼 보이지만 절차 적 시스템을 원하거나 실제로 수업을 이해 하지 못하면 프로젝트를 잠 가야합니다.

기본적으로 oo 언어로 작성하려는 경우 프로젝트는 확장을 포함하는 oo 패러다임을 따라야합니다. 물론 누군가가 다른 패러다임에 도서관을 썼다면 어쨌든 그것을 확장하지 않아야 할 것입니다.


답변

그들은 더 잘 알지 못하기 때문에.

원작자는 아마도 혼란스럽고 복잡한 C ++ 세계에서 비롯된 SOLID 원칙에 대한 오해에 집착하고있을 것입니다.

루비, 파이썬 및 펄 세계에는 여기에 대한 답변이 봉인의 이유라고 주장하는 문제가 없다는 것을 알기를 바랍니다. 동적 타이핑과 직교한다는 점에 유의하십시오. 액세스 수정자는 대부분의 언어에서 쉽게 사용할 수 있습니다. C ++ 필드는 다른 유형으로 캐스트하여 막힐 수 있습니다 (C ++가 더 약합니다). Java와 C #은 리플렉션을 사용할 수 있습니다. 액세스 수정자는 실제로 원하지 않는 한 작업을 수행 할 수 없을 정도로 어렵게 만듭니다.

봉인 강의와 모든 회원을 비공개로 표시하는 것은 간단한 일이 단순하고 어려운 일이 가능해야한다는 원칙을 명백히 위반합니다. 갑자기 단순해야 할 것들이 아닙니다.

원저자의 관점을 이해하도록 권유합니다. 현실 세계에서 절대적인 성공을 보여주지 못한 캡슐화에 대한 학문적 아이디어에서 비롯된 것입니다. 어딘가 개발자가 약간 다르게 작동하기를 원하지 않고 변경해야 할 이유가없는 프레임 워크 또는 라이브러리를 본 적이 없습니다. 회원을 봉인하고 비공개로 만든 최초의 소프트웨어 개발자를 괴롭 혔을 가능성이 두 가지 있습니다.

  1. 오만-그들은 확장을 위해 개방되고 수정을 위해 폐쇄되었다고 정말로 믿었습니다.
  2. 만족-그들은 다른 유스 케이스가있을 수 있다는 것을 알았지 만 그 유스 케이스에 대해서는 쓰지 않기로 결정했습니다

회사 프레임 워크에서 # 2가 아마도 그럴 것이라고 생각합니다. 이러한 C ++, Java 및 .NET 프레임 워크는 “완료”되어야하며 특정 지침을 따라야합니다. 이러한 지침은 일반적으로 유형이 유형 계층 구조의 일부로 명시 적으로 설계되고 다른 용도에 유용 할 수있는 많은 것들에 대한 개인 구성원이 아닌 한 봉인 된 유형을 의미합니다. . 새로운 유형을 추출하는 것은 지원, 문서화 등을하기에는 너무 비쌉니다.

액세스 수정 자의 기본 개념은 프로그래머가 스스로 보호해야한다는 것입니다. “C 프로그래밍은 발로 자신을 쏠 수 있기 때문에 나쁘다.” 프로그래머로서 동의하는 것은 철학이 아닙니다.

나는 파이썬의 이름 맹 글링 접근법을 선호합니다. 필요한 경우 개인을 쉽게 대체 할 수 있습니다 (반사보다 훨씬 쉽습니다). 그것에 대한 훌륭한 글은 여기에서 볼 수 있습니다 : http://bytebaker.com/2009/03/31/python-properties-vs-java-access-modifiers/

Ruby의 개인용 수정자는 실제로 C #에서 보호되는 것과 비슷하며 개인용 C # 수정 자도 없습니다. 보호는 조금 다릅니다. 여기에 좋은 설명이 있습니다 : http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/

정적 언어는 해당 언어로 작성된 코드의 구식 스타일을 따를 필요가 없습니다.