객체 지향 클래스 디자인


12

좋은 객체 지향 클래스 디자인이 궁금합니다. 특히, 나는이 옵션들 사이를 결정하는 데 어려움을 겪고 있습니다.

  1. 정적인스턴스 방법
  2. 매개 변수가없는 메소드 또는 리턴 값매개 변수가있는 메소드 및 리턴 값
  3. 중복고유 한 방법 기능
  4. 개인공개 방법

예 1 :

이 구현은 반환 값이나 매개 변수가없고 겹치는 기능이없고 모든 메서드가 공용 인 인스턴스 메서드를 사용합니다.

XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();

예 2 :

이 구현은 겹치는 기능 및 개인 메소드와 함께 리턴 값 및 매개 변수와 함께 정적 메소드를 사용합니다.

Document result = XmlReader.readXml(url); 

예를 들어, 모든 메소드는 퍼블릭 인스턴스이므로 단위 테스트가 용이합니다. 모든 메소드는 고유하지만 readXml ()은 openUrl ()에 종속적이며 openUrl ()을 먼저 호출해야합니다. 모든 데이터는 인스턴스 필드에 선언되므로 생성자와 접근자를 제외하고 모든 메서드에 반환 값이나 매개 변수가 없습니다.

예제 2에서는 하나의 메서드 만 공개하고 나머지는 비공개 정적이므로 단위 테스트가 어렵습니다. readXml ()에서 openUrl ()을 호출하는 메소드가 겹칩니다. 필드가 없으며 모든 데이터가 메소드의 매개 변수로 전달되고 결과가 즉시 리턴됩니다.

적절한 객체 지향 프로그래밍을하려면 어떤 원칙을 따라야합니까?


3
멀티 스레딩을 수행 할 때 정적 문제가 발생합니다. 다른 날에는 XMLWriter.write (data, fileurl)과 같은 정적 XMLWriter가있었습니다. 그러나 개인 정적 FileStream을 가지고 있기 때문에 동시에 여러 스레드에서이 클래스를 사용하면 두 번째 스레드가 첫 번째 스레드 FileStream을 겹쳐 쓰므로 오류를 찾기가 매우 어려웠습니다. 정적 멤버 + 멀티 스레딩이있는 정적 클래스는 재난을위한 레시피입니다.
Per Alexandersson

1
@Paxinum. 설명하는 문제는 "정적"문제가 아니라 상태 문제입니다. 비 정적 멤버에 싱글 톤을 사용한 경우 멀티 스레딩과 동일한 문제가 발생합니다.
mike30

2
@Per Alexandersson 정적 메서드는 동시성과 관련하여 나쁘지 않습니다. 정적 상태가 좋지 않습니다. 모든 메소드가 정적 인 기능 프로그래밍이 동시 상황에서 매우 잘 작동하는 이유입니다.
Yuli Bonner

답변:


11

예제 2는 테스트하기에 매우 좋지 않습니다 ... 내가 테스트 할 수 없다는 것을 의미하지는 않습니다. XmlReader객체가 없기 때문에 객체를 모의 객체로 바꿀 수 없습니다 .

예제 1은 불필요하게 사용하기가 어렵습니다. 는 어때

XmlReader reader = new XmlReader(url);
Document result = reader.getDocument();

정적 메소드보다 사용하기가 어렵지 않습니다.

URL 열기, XML 읽기, 바이트를 문자열로 변환, 구문 분석, 소켓 닫기 등은 흥미롭지 않습니다. 객체를 생성하고 사용하는 것이 중요합니다.

따라서 IMHO의 적절한 OO 디자인은 두 가지만 공개하는 것입니다 (어떤 이유로 중간 단계가 실제로 필요하지 않은 한). 정적은 악하다.


-1. 실제로 XmlReader를 모의 객체로 바꿀 수 있습니다. 브레인 데드 오픈 소스 moick 프레임 워크가 아니라 산업 등급이 좋은 프레임 워크를 사용할 수 있습니다.
TomTom

2
TomTom의 판매 피치에 불응하지 않은 경우 +1 하나의 라이너를 원할 때 나는 Document result = new XmlReader(url).getDocument();왜 Why? 그래서 나는 그것을 업그레이드 할 수 있다고 Document result = whateverReader.getDocument();하고있다 whateverReader뭔가 다른 나에게 주었다.
candied_orange

6

여기에 문제가 있습니다. 정답은 없으며 "적절한 객체 지향 디자인"에 대한 절대적인 정의도 없습니다.

그것은 모두 당신의 목표에 달려 있습니다.

당신은 예술가이며 종이는 비어 있습니다. 섬세하고 정교하게 연필로 칠한 흑백 인물 사진 또는 혼합 네온의 거대한 가스가있는 추상 그림을 그릴 수 있습니다. 또는 그 사이의 어떤 것.

그래서 당신이 해결하고있는 문제에 어떤 느낌이 드는가? xml 작업을 위해 클래스를 사용해야하는 사람들의 불만은 무엇입니까? 그들의 직업에 대해 어려운 것은 무엇입니까? 라이브러리에 대한 호출을 둘러싸고 있는 어떤 종류의 코드를 작성하려고 합니까?

간결함을 더 원하십니까? 그들은 매개 변수의 기본값을 알아내는 데 매우 영리하기를 원하므로 많은 것을 지정할 필요가 없으며 올바르게 추측 할 수 있습니까? 라이브러리에서 필요한 단계를 잊을 수 없도록 설정 및 정리 작업을 자동화 할 수 있습니까? 그들을 위해 또 무엇을 할 수 있습니까?

지옥, 아마도 당신이해야 할 일은 4 또는 5 가지 방법으로 코딩 한 다음 소비자 모자를 쓰고 5 가지를 모두 사용하는 코드를 작성하고 어떤 느낌이 더 좋은지 확인하십시오. 전체 라이브러리에 대해이를 수행 할 수없는 경우 서브 세트에 대해 수행하십시오. 또한 유창한 인터페이스,보다 기능적인 접근 방법, 명명 된 매개 변수 또는 DynamicObject를 기반으로하는 목록에 대한 추가 대안을 목록에 추가해야합니다. 밖?

왜 jQuery가 왕입니까? Resig와 팀은이 프로세스를 따랐기 때문에 돔과 이벤트에 사용되는 JS 코드의 양 을 엄청나게 줄인 구문 원리를 발견 했습니다. 그 구문 원리는 그들이나 다른 사람들이 처음 시작할 때 명확하지 않았습니다. 그들은 그것을 발견했다.

프로그래머로서 이것이 가장 높은 부름입니다. 당신이 그것을 찾을 때까지 당신은 어두운 노력의 물건 주위에 모색. 당신이 할 때, 당신은 알게 될 것입니다. 그리고 사용자에게 생산성을 크게 향상 시킬 수 있습니다 . 그리고 이것이 바로 소프트웨어 영역의 디자인에 관한 것입니다.


1
이는 모범 사례와 "느낌"이외의 다른 사례간에 차이가 없음을 의미합니다. 이것은 많은 개발자들에게 클래스 경계를 ​​넘나 드는 등의 이유로 유지 관리 할 수없는 진흙 덩어리를 만드는 방법입니다.
Amy Blankenship

@Amy Blankenship, 나는 OP가 요구하는 선택을 할 수있는 "최상의 방법"이 없다고 분명히 말할 것이다. 그것은 백만 가지에 달려 있으며 백만도의 자유가 있습니다. 그러나 "모범 사례"를위한 장소가 있다고 생각합니다. 환경에는 특정 선택 이 이미 이루어졌으며 이전 팀의 선택과 일관성을 유지하기 위해 나머지 팀이 필요합니다. 다시 말해서, 특정한 맥락에서 , 어떤 것들을 "모범 사례"라고 표시해야하는 이유가있을 수 있습니다. 그러나 OP는 컨텍스트를 제공하지 않았습니다. 그는 무언가를 만들고 있습니다 ...
Charlie Flowers

... 그는 가능한 모든 선택에 직면 해 있습니다. 그러한 선택에 대한 "올바른 대답"은 없습니다. 그것은 시스템의 목표와 고통 지점에 의해 좌우됩니다. Haskell 프로그래머가 모든 메소드가 인스턴스 메소드라고 생각하지는 않습니다. 그리고 리눅스 커널 프로그래머들은 TDD에 접근 할 수있게 만드는 것이 전혀 중요하지 않다고 생각합니다. 그리고 C ++ 게임 프로그래머는 종종 모든 것을 객체로 캡슐화하는 것보다 데이터를 메모리의 단단한 데이터 구조로 묶는 것이 좋습니다. 모든 "모범 사례"는 특정 상황에서 "모범 사례"일 뿐이며 다른 상황에서는 반 패턴입니다.
Charlie Flowers

@AmyBlankenship 한 가지 더 : 나는 클래스 경계를 ​​넘어 도달하는 것이 "정말 놀라운 느낌"이라고 동의하지 않습니다. 그것은 유지 불가능한 진흙 공으로 이어지고, 그것은 끔찍한 느낌 입니다. 일부 근로자가 조잡하거나 동기가 없거나 경험이 부족한 문제를 해결하려고한다고 생각합니다. 이 경우, 신중하고 동기 부여가되고 경험이 풍부한 사람이 중요한 선택을하고 "최상의 실습"이라고해야합니다. 그러나 이러한 "모범 사례"를 선택하는 사람은 여전히 "느낌"에 따라 선택을 하고 있으며 정답은 없습니다. 당신은 누가 선택을 하는지 통제하고 있습니다 .
Charlie Flowers

나는 스스로 자신을 상급자라고 생각한 몇 명의 프로그래머와 함께 일했으며 정적과 싱글 톤이 통신 문제를 처리하는 올바른 방법이라고 굳게 믿었던 경영진에 의해 그렇게 생각했습니다. 이 질문의 정적 부분은 개발자에게 "느낌"과 같은 클래스 경계를 ​​넘어서서 도달하거나 정적 대안을 옹호하는 답변에 대한 투표가 이루어지지 않았는지조차 묻지 않았을 것입니다.
Amy Blankenship

3

두 번째 옵션은 사람들이 사용하기가 더 간단하기 때문에 (더보기 만해도) 훨씬 더 좋습니다.

단위 테스트의 경우 실제로 내부를 개인에서 보호로 옮기려면 내부가 아닌 인터페이스를 테스트합니다.


2
테스트 케이스가 동일한 패키지에있는 경우 단위 테스트 목적으로 메소드를 패키지 전용 (기본값)으로 만들 수도 있습니다.

좋은 지적입니다.

3

1. 정적 대 인스턴스

좋은 OO 디자인과 그렇지 않은 것에 대해 매우 명확한 지침이 있다고 생각합니다. 문제는 블로고 스피어가 선과 악을 추악하게 구분하는 것을 어렵게한다는 것이다. 당신은 찾을 수있는 몇 가지 당신이 생각할 수있는 최악의 실천을 지원하는 레퍼런스의 종류.

제가 생각할 수있는 최악의 사례는 언급 한 통계와 모든 사람이 좋아하는 싱글 톤을 포함하여 전역 상태입니다. Misko Hevery의 주제에 대한 고전 기사 의 일부 발췌 .

종속성을 실제로 이해하려면 개발자는 모든 코드 줄을 읽어야합니다. 테스트 스위트를 실행할 때 한 테스트에서 글로벌 상태가 변경되면 후속 또는 병렬 테스트가 예기치 않게 실패 할 수 있습니다. 수동 또는 Guice 종속성 주입을 사용하여 정적 종속성을 해제하십시오.

거리에서의 무시 무시한 행동은 (우리가 참조를 전달하지 않았기 때문에) 고립 된 것으로 판단되는 한 가지를 실행하지만 예상치 못한 상호 작용과 상태 변경은 우리가 물체에 대해 말하지 않은 시스템의 먼 위치에서 발생합니다. 이것은 글로벌 상태를 통해서만 발생할 수 있습니다.

이전에는 이런 식으로 생각하지 않았지만 정적 상태를 사용할 때마다 비밀 통신 채널을 작성하고 API에서 명확하게 표시하지 않습니다. 거리에서의 무시 무시한 행동으로 인해 개발자는 모든 코드를 읽어 잠재적 인 상호 작용을 이해하고 개발자 생산성을 낮추며 새로운 팀원을 혼란스럽게합니다.

이것이 요약되는 것은 일종의 저장된 상태를 가진 것에 대한 정적 참조를 제공해서는 안된다는 것입니다. 내가 정적을 사용하는 유일한 장소는 열거 된 상수에 대한 것이며, 그에 대한 오해가 있습니다.

2. 입력 매개 변수가있는 메소드와 리턴 값이없는 메소드

입력 매개 변수가없고 출력 매개 변수가없는 메소드는 내부적으로 저장된 상태 (즉, 수행중인 작업)에서 작동하도록 보장됩니다. 있습니다 전체 언어 저장 상태를 피할 수있는 아이디어에 내장되어 있습니다.

상태를 저장할 때마다 부작용이 발생할 가능성이 있으므로 항상주의해서 사용해야합니다. 이는 정의 된 입력 및 / 또는 출력 기능을 선호 해야 함을 의미합니다 .

실제로 입력과 출력을 정의한 함수는 테스트하기가 훨씬 쉽습니다. 여기서 함수를 실행할 필요가 없습니다. 어떤 일이 있었는지 확인하기 위해 속성을 설정할 필요가 없습니다. 그렇지 않으면 테스트중인 함수를 실행하기 전에

이 유형의 함수를 정적으로 안전하게 사용할 수도 있습니다 . 그러나 나중에 새로운 구현으로 다른 인스턴스를 제공하는 대신 어딘가에서 해당 기능의 약간 다른 구현을 사용하고 싶을 경우 기능을 대체 할 수있는 방법이 없습니다.

3. 중복 대 구별

질문을 이해하지 못합니다. 두 가지 겹치는 방법의 장점은 무엇입니까?

4. 개인 대 공개

노출 할 필요가없는 것을 노출시키지 마십시오. 그러나 나는 또한 사적인 팬이 아닙니다. 저는 C # 개발자가 아니라 ActionScript 개발자입니다. 저는 2007 년경에 작성된 Adobe의 Flex Framework 코드에서 많은 시간을 보냈습니다. 그리고 그들은 비공개로 만들 내용을 정말 잘못 선택하여 수업을 확장하려는 악몽이되었습니다.

따라서 2007 년경 Adobe 개발자보다 건축가가 더 좋다고 생각하지 않는 한 (질문을 제기하기 전에 몇 년이 더 있다고 말할 수 있습니다.) .


코드 예제에 약간의 문제가있어 아키텍처가 잘못되어 A 또는 B를 선택할 수 없습니다.

우선, 객체 생성과 사용을 분리 해야 할 것입니다 . 따라서 일반적으로 new XMLReader()사용되는 곳 옆에 권리 가 없습니다 .

또한 @djna가 말했듯이 XML 리더 사용에 사용 된 메소드를 캡슐화해야 API (인스턴스 예제)가 다음과 같이 단순화 될 수 있습니다.

_document Document = reader.read(info);

C #의 작동 방식을 모르지만 여러 웹 기술을 사용해 본 결과 약속 또는 미래 유형을 제외하고는 항상 XML 문서를 즉시 반환 할 수는 없을 것입니다. C #에서 비동기로드를 처리하는 방법에 대한 조언을 드릴 수는 없습니다.

이 접근 방식을 사용하면 XML 객체를 읽고 반환 할 위치 / 대상을 알려주는 매개 변수를 가져와 프로젝트 요구에 따라 스왑 할 수있는 몇 가지 구현을 작성할 수 있습니다. 예를 들어, 데이터베이스, 로컬 상점 또는 원래 예제와 같이 URL에서 직접 읽을 수 있습니다. 정적 메소드를 사용하면 그렇게 할 수 없습니다.


2

고객의 관점에 중점을 둡니다.

IReader reader = new XmlReader.readXml(url);  // or injection, or factory or ...
Document document = reader.read();

정적 메소드는 미래의 진화를 제한하는 경향이 있으며, 클라이언트는 다양한 구현으로 제공되는 인터페이스 측면에서 작업하고 있습니다.

개방형 / 판독 형 관용구의 주요 문제점은 클라이언트가 간단한 작업 만 수행하려는 경우 메소드를 호출하는 순서를 알아야한다는 것입니다. 여기서는 분명하지만 더 큰 클래스에서는 분명하지 않습니다.

테스트 할 기본 메소드는 read ()입니다. 프로그램을 공개 또는 비공개로 만들지 않고 동일한 패키지에 테스트를 넣어서 내부 메소드를 테스트 프로그램에 표시 할 수 있습니다. 테스트는 릴리스 된 코드와 별도로 유지 될 수 있습니다.


테스트 스위트가 다른 프로젝트에있는 경우 기본 가시성을 가진 메소드가 계속 표시됩니까?
siamii

자바는 프로젝트에 대해 모른다. 프로젝트는 IDE 구조입니다. 컴파일러와 JVM은 테스트 및 테스터 클래스가있는 패키지를 확인합니다. 동일한 패키지이며 기본 표시가 허용됩니다. Eclipse에서 두 개의 다른 소스 디렉토리가있는 단일 프로젝트를 사용합니다. 방금 두 개의 프로젝트로 시도했지만 작동합니다.

2

정적 대 인스턴스 방법

실제로 정적 메소드는 일반적으로 유틸리티 클래스에 국한되며 도메인 오브젝트, 관리자, 컨트롤러 또는 DAO를 혼동해서는 안됩니다. 정적 메소드는 필요한 모든 참조가 합리적으로 매개 변수로 전달되고 많은 클래스에서 재사용 할 수있는 일부 기능을 제공 할 수있을 때 가장 유용합니다. 인스턴스 객체에 대한 참조를위한 해결 방법으로 정적 메서드를 사용하고 있다면 왜 그 참조가 없는지 스스로에게 물어보십시오.

매개 변수가없는 메소드 또는 리턴 값 대 매개 변수가있는 메소드 및 리턴 값

메소드에 매개 변수가 필요하지 않은 경우 추가하지 마십시오. 리턴 값도 마찬가지입니다. 이것을 염두에두면 코드가 단순화되고 결코 일어나지 않는 수많은 시나리오를 코딩하지 않아도됩니다.

중복 대 고유 한 방법 기능

중복되는 기능을 피하는 것이 좋습니다. 때로는 어려울 수 있지만 논리 변경이 필요한 경우 비슷한 기능을 가진 전체 메소드를 변경하는 것보다 재사용되는 메소드를 변경하는 것이 훨씬 쉽습니다.

개인 대 공개 방법

일반적으로 게터, 세터 및 생성자는 공개되어야합니다. 다른 클래스가 실행 해야하는 경우가 아니라면 비공개로 유지하려고하는 모든 것. 메소드의 기본값을 private으로 유지하면 캡슐화 를 유지하는 데 도움이됩니다 . 필드도 마찬가지입니다. 기본적으로 비공개로 익숙해집니다.


1

귀하의 질문에 대답하지는 않지만 귀하가 사용한 용어로 문제가 발생했다고 생각합니다. 예 :

XmlReader.read => twice "read"

나는 당신이 XML이 필요하다고 생각하므로 텍스트 형식에서 만들 수있는 객체 XML을 만들 것입니다 (Java에서는 C #을 알지 못합니다 .String이라고합니다). 예 :

class XML {
    XML(String text) { [...] }
}

당신은 그것을 테스트 할 수 있으며 분명합니다. 그런 다음 팩토리가 필요한 경우 팩토리 메소드를 추가 할 수 있습니다 (두 번째 예제와 같이 정적 일 수 있음). 예 :

class XML {
    XML(String text) { [...] }

    static XML fromUrl(url) { [...] }

}

0

간단한 규칙을 따를 수 있습니다. 규칙의 이유를 이해하면 도움이됩니다.

정적 대 인스턴스 방법

방법의 경우이 결정을 의식적으로 내릴 필요는 없습니다. 메서드가 필드 멤버를 사용하지 않는 것 같으면 (좋아하는 분석기에서 알려 주어야 함) 정적 키워드를 추가해야합니다.

매개 변수가없는 메소드 또는 리턴 값 대 매개 변수가있는 메소드 및 리턴 값

두 번째 옵션은 범위 때문에 더 좋습니다. 항상 스코프를 단단히 유지해야합니다. 필요한 것에 도달하는 것은 좋지 않습니다. 입력이 있어야하고 로컬로 작업하여 결과를 반환해야합니다. 첫 번째 옵션은 전역 변수가 일반적으로 나쁘기 때문에 같은 이유로 나쁩니다. 코드의 일부에만 의미가 있지만 다른 곳에서는 볼 수 있으므로 노이즈가 발생하여 어디에서나 조작 될 수 있습니다. 이것은 당신의 논리를 완전히 이해하기 어렵게 만듭니다.

중복 대 고유 한 방법 기능

나는 이것이 문제라고 생각하지 않습니다. 다른 방법을 호출하는 방법은 작업을 더 작은 기능성 덩어리로 자르면 좋습니다.

개인 대 공개 방법

공개하지 않으려면 모든 것을 비공개로 만드십시오. 수업의 사용자는 소음없이 할 수 있으며 자신에게 중요한 것만보고 싶어합니다.

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