2009-06-25 5 views
17

이것은 매우 기본적인 물건입니다. 그러나 여기에 있습니다. 나는 내가 자신이 큰 클래스를 작은 것들로 나누는 방법이 인지에 대해 결코 동의 할 수 없다는 것을 알게되었다. 일들을 유지 보수가 용이하거나 유지하기가 더 어렵게 만든다. 나는 디자인 패턴에 익숙하지만, 상세하지는 않지만 의 객체 지향 디자인의 개념도 있습니다. 모든 멋진 규칙과 지침은 제쳐두고, 아주 간단한 샘플 시나리오로 내가 놓친 것에 관해서 당신의 두뇌를 선택하려고합니다. 본질적으로 다음과 같은 라인을 따라 : "...이 디자인은 더 어렵게 만들 것입니다", 등등 ... 나는 경험의 부족으로 인해 본질적으로 기대하지 않는 모든 것들.코드를 구성 요소로 분할하는 방법 ... 큰 클래스? 작은 수업?

특정 파일 형식을 처리하기 위해 기본적인 "파일 판독기/파일 작성기"스타일 클래스를 작성해야한다고 가정하면 파일을 YadaKungFoo 파일이라고합시다. YadaKungFoo 파일의 내용은 본질적으로 INI 파일과 유사하지만 미묘한 차이가 있습니다.

섹션 및 값이있다 :

[Sections] 
Kung,Foo 
Panda, Kongo 

[AnotherSection] 
Yada,Yada,Yada 
Subtle,Difference,Here,Yes 

[Dependencies] 
PreProcess,SomeStuffToPreDo 
PreProcess,MoreStuff 
PostProcess,AfterEight 
PostProcess,TheEndIsNear 
PostProcess,TheEnd 

OK 그래서 이것은 3 개 기본 클래스를 생성 할 수있다하십시오 ToString() 재정의로

public class YadaKungFooFile 
public class YadaKungFooFileSection 
public class YadaKungFooFileSectionValue 

두 후자 클래스는 필수적으로 데이터 구조 두 개의 일반 목록을 사용하여 저장된 값의 문자열 목록을 뱉어 내십시오. 이것은 YadaKungFooFile 저장 기능을 구현하기에 충분합니다.

시간이 지남에 따라 YadaYada 파일이 커지기 시작합니다. 여러 가지 오버로드가 XML 등을 포함한 다양한 형식으로 저장되며 파일은 800 줄 정도가됩니다. 이제 진짜 질문 : YadaKungFoo 파일의 내용을 검증하는 기능을 추가하고 싶습니다. 가장 먼저 생각해보아야 할 것은 분명히 다음과 같습니다.

var yada = new YadaKungFooFile("C:\Path"); 
var res = yada .Validate() 

이제 완료되었습니다 (생성자에서 해당 메소드를 호출 할 수도 있습니다). 이제이 샘플 분명히 정말 간단하고 사소한 볼품입니다

var yada = new YadaKungFooFile("C:\Path"); 
var validator = new YadaKungFooFileValidator(yada); 
var result = validator.Validate(); 

: 문제는, 검증은 매우 복잡하고, 클래스가 매우 큰 수, 그래서 우리는이 같은 새로운 클래스를 생성하기로 결정. 아마 위의 두 가지 방법 중 하나를 너무 많이 차이가 있지만, 내가 좋아하지 않는 것은하지 않습니다

  1. YadaKungFooFileValidator 클래스와 YadaKungFooFile 클래스는 매우 강하게이 디자인에 의해 결합 될 것 같다 . 한 클래스에서 변경된 것처럼 보이고 다른 클래스에서 변경을 트리거 할 가능성이 있습니다.
  2. "Validator", "Controller", "Manager"등과 같은 문구는 "자체 사업"과 반대되는 다른 대상의 상태와 관련된 클래스를 나타내며 따라서 분리를 위반한다는 것을 알고 있습니다. 관심 원칙 및 메시지 전송

나는 디자인이 왜 나쁜지에 대한 모든 측면을 이해할 수있는 경험이 없다고 생각합니다. 어떤 상황에서 실제로는 중요하지 않으며 관심사에는 더 많은 무게가 있습니다. 수업 또는보다 응집력있는 수업. 그들은 모순적인 요구 사항 인 것 같지만 아마도 틀 렸습니다.어쩌면 유효성 검사기 클래스는 복합 개체 여야합니까?

본질적으로 위의 설계로 인해 발생할 수있는 이점/문제에 대한 의견을 묻습니다. 다르게 할 수있는 일은 무엇입니까? (기본 FileValidator 클래스, FileValidator 인터페이스 등 .. 이름은 그것). YadaKungFooFile 기능은 시간이 지남에 따라 계속 증가하고 있다고 생각하십시오.

답변

15

밥 마틴은 자신이 SOLID 원칙으로 의미 클래스 디자인에 대한 일련의 기사를 썼다 :

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

원칙은 다음과 같습니다

  1. 단일 책임 원칙 : 계급은 변화를 가져야하는 이유가 하나뿐입니다.
  2. 오픈 클로즈드 원칙 : 클래스 동작을 수정하지 않고 확장 할 수 있어야합니다.
  3. The Liskov Substitution Principle : 파생 클래스는 기본 클래스로 대체 할 수 있어야합니다.
  4. 인터페이스 분리 원칙 : 클라이언트별로 세분화 된 인터페이스를 만듭니다.
  5. 종속성 반전 원리 : 콘크리트에 의존하지 않고 추상화에 의존합니다.

그래서 사람들의 빛,의이 문장의 일부를 살펴 보자 :

그래서 시간이 지남에 YadaYadaFile 성장을 시작합니다. 여러 오버로드 등 XML 등 다양한 형식으로 저장하고, 파일이 800 선을 향해 밀어 시작 정도

이 첫 번째 빅 레드 플래그 : 1) 섹션/키를 유지 : YadaYadaFile 두 책임로 시작/value 데이터 구조, 2) INI와 유사한 파일을 읽고 쓰는 방법을 알고 있어야합니다. 그래서 첫 번째 문제가 있습니다. 더 많은 파일 형식을 추가하면 YadaYadaFile이 1) 데이터 구조가 변경되거나 2) 파일 형식이 변경되거나 3) 새로운 파일 형식이 추가 될 때 변경됩니다.

마찬가지로, 세 가지 책임 모두에 대해 단일 유효성 검사기를 사용하면 해당 단일 클래스에 너무 많은 책임을지게됩니다.

큰 클래스는 "코드 냄새"입니다. 그 자체로 나쁘지는 않지만 실제로는 너무 나쁜 것에서 비롯되는 경우가 많습니다.이 경우에는 너무 많은 것을 시도하는 클래스입니다.

2

나는 YadaKungFooFile 클래스는 매우 강하게이 디자인에 의해 결합 것 같습니다이

YadaKungFooFileValidator 클래스와 를 해결할 수 있습니다. 한 클래스에서 변경된 것 같습니다. 은 다른 클래스에서 트리거가 변경 될 가능성이 높습니다.

예, 그렇다고해서 가능한 한 매끄럽게 만드는 방법을 찾아보십시오. 가장 좋은 경우에는 YadaKungFooFile 클래스를 설계하여 유효성 검사기가 자체에서 해당 값을 선택할 수 있도록하십시오.

먼저 구문 자체를 매우 쉽게 작성해야합니다. 구문 검증이 바뀌기를 기대하지 않기 때문에 코드를 하드 코딩하는 것이 안전 할 것입니다.

허용되는 속성까지는 유효성 검사기가 검사 할 File 클래스의 열거자를 노출 할 수 있습니다. 구문 분석 된 값이 주어진 섹션의 열거 자에없는 경우 예외를 throw합니다.

등등 ...

14

클래스의 크기가 중요하지 않다고 생각합니다. 관심은 더 많은 응집력과 결합력입니다. 느슨하게 결합되고 응집력있는 오브젝트를 디자인하려고합니다. 즉, 그들은 잘 정의 된 것에 관심을 가져야합니다. 그래서 일이 매우 복잡하다면 수업은 성장할 것입니다.

다양한 디자인 패턴을 사용하여 복잡성을 관리 할 수 ​​있습니다. 예를 들어 할 수있는 한 가지 방법은 Validator 인터페이스를 만들고 YadaKunfuFile 클래스를 Validator가 아닌 인터페이스에 종속시키는 것입니다. 이렇게하면 인터페이스가 변경되지 않는 한 YadaKungfuFile 클래스를 변경하지 않고도 Validator 클래스를 변경할 수 있습니다.

+4

"클래스의 크기가 걱정 스럽다고 생각하지 않습니다." 이론적으로 나는 동의하고 싶다. 실제로 나는 내가 좋아하는 2,000 개 이상의 라인을 가진 수업을 본 적이 없다. – PeterAllenWebb

+1

@PeterAllenWebb ... 동의합니다. 그러나 LOC의 수는 결정적인 요소가 아니어야합니다. 클래스의 의미가 클래스가 한 가지 또는 두 가지 이상의 것에 관련되어 있는지 판단하는 데 도움이됩니다. 실제로 대부분의 수업은 작을 것입니다. –

+1

@ Vincent Ramdhanie LOC는 냄새의 정말 좋은 지표입니다. 나의 일반적인 규칙은 입증 된 무고한 500 LOC 클래스까지 –

0

이 경우 개인적으로 처리하는 방법은 YadaKungFooFileSections 목록 (YadaKungFooFileValues ​​목록)으로 구성된 YadaKungFooFile 클래스를 갖는 것입니다.

유효성 검사기의 경우 각 클래스는 그 아래 클래스의 validate 메서드를 호출하게됩니다. 계층 구조에서 가장 낮은 클래스는 유효성 검사 메서드의 실제 문자열을 구현합니다.

0

유효성 검사가 전체 파일에 의존하지 않고 대신 개별 섹션 값에서만 작동하는 경우 섹션 값에서 작동하는 유효성 검사기 집합을 지정하여 YadaKungFooFile 클래스의 유효성 검사를 분해 할 수 있습니다.

유효성 검사가 전체 파일에 의존하는 경우 유효성 검사는 자연스럽게 파일 클래스에 결합되므로 하나의 클래스에 넣을 것을 고려해야합니다.

0

클래스 크기는 메소드 크기만큼 문제가되지 않습니다. 클래스는 조직의 수단이며 코드를 구성하는 좋은 방법과 나쁜 방법이 있지만 클래스 크기에 직접 연결되지는 않습니다.

메서드가 실제 작업을 수행하기 때문에 메서드 크기가 다릅니다. 방법은 불가피하게 그들의 크기를 제한 할 아주 특정한 일이 있어야한다.

메소드 크기를 결정하는 가장 좋은 방법은 단위 테스트를 작성하는 것입니다. 코드 커버리지가 충분한 단위 테스트를 작성하는 경우 방법이 너무 커지면 분명해진다.

단위 테스트 방법은 간단해야합니다. 그렇지 않으면 단위 테스트가 제대로 작동하는지 테스트하기 위해 테스트가 필요합니다. 단위 테스트 방법이 복잡 해지면 메소드가 너무 많이 수행하려고 시도하고 명확한 책임을 지닌 더 작은 메소드로 분해되어야하기 때문일 수 있습니다.

4

YadaKungFooFile은 디스크에서 자신을 읽는 방법을 알아야합니다. 그것 자체를 탐색하는 방법을 알고 그 자녀 등을 공개해야합니다. 또한 어린이 추가/제거하는 방법을 제공해야합니다.

YadaKungFooFile이 자신을로드하는 데 사용할 Load 메서드에서 취할 IYadaKungFooReader 인터페이스가 있어야합니다. 디스크에서. 또한 AbstractKungFooReader, PlainTextYadaKungFooReader, XMLYadaKungFooWriter와 같은 여러 구현을 통해 읽기 및 해당 형식을 인식합니다.

글쓰기와 동일합니다.

마지막으로 IYadaKungFooReader 판독기를 가져 오는 YadaKungFooValidatingReader가 있어야하며 읽음으로 입력을 읽고 유효성을 검사하는 데 사용하십시오. 그런 다음 유효성 검사를하는 판독기를 디스크에서 읽을 때 유효성을 검사 할 때마다 YadaKungFooFile.Load에 전달합니다.

또는 수동 클래스 대신 판독기를 활성화 할 수 있습니다. 그런 다음 YadaKungFooFile에 전달하는 대신 후자의 일반적인 액세스 방법을 사용하여 YadaKungFooFile을 만드는 공장으로 사용할 것입니다. 이 경우 리더는 YadaKungFooFile 인터페이스를 구현하여 일반 리더 -> 유효성 검사 리더 -> YadaKungFooFile을 연결할 수 있도록해야합니다. 말이된다?

관련 문제