SOLID 원리 적용


13

저는 SOLID 설계 원칙에 익숙하지 않습니다 . 나는 그들의 원인과 이점을 이해하지만 SOLID 원칙을 사용하기위한 실제 연습으로 리팩토링하려는 더 작은 프로젝트에는 적용하지 않습니다. 완벽하게 작동하는 응용 프로그램을 변경할 필요는 없지만 어쨌든 리팩터링하여 향후 프로젝트의 디자인 경험을 얻고 싶습니다.

응용 프로그램에는 다음과 같은 작업이 있습니다 (실제로는 그 이상이지만 간단하게 유지하십시오). 데이터베이스 테이블 / 열 /보기 등 정의가 포함 된 XML 파일을 읽고 작성하기 위해 사용할 수있는 SQL 파일을 만들어야합니다 ORACLE 데이터베이스 스키마

(참고 : 왜 필요한지 또는 왜 XSLT 등을 사용하지 않는지에 대한 논의는 자제하십시오. 이유는 있지만 주제가 맞지 않습니다.)

처음에는 테이블과 제약 조건 만 보도록 선택했습니다. 열을 무시하면 다음과 같이 말할 수 있습니다.

제약 조건은 테이블의 일부 (또는 CREATE TABLE 문의 일부)이며 제약 조건이 다른 테이블을 참조 할 수도 있습니다.

먼저 SOLID를 적용하지 않고 현재 응용 프로그램이 어떻게 보이는지 설명하겠습니다.

현재이 응용 프로그램에는 테이블이 소유 한 제약 조건에 대한 포인터 목록과이 테이블을 참조하는 제약 조건에 대한 포인터 목록이 포함 된 "테이블"클래스가 있습니다. 연결이 설정 될 때마다 역방향 연결도 설정됩니다. 테이블에는 createStatement () 메소드가 있으며, 각 메소드의 createStatement () 함수를 호출합니다. 상기 방법은 그 이름을 검색하기 위해 소유자 테이블 및 참조 테이블에 대한 연결을 사용한다.

분명히 이것은 SOLID에는 전혀 적용되지 않습니다. 예를 들어, 필요한 "add"/ "remove"메소드와 일부 대형 오브젝트 소멸자 측면에서 코드를 부풀린 순환 종속성이 있습니다.

따라서 몇 가지 질문이 있습니다.

  1. Dependency Injection을 사용하여 순환 종속성을 해결해야합니까? 그렇다면 제약 조건이 생성자에서 소유자 (및 선택적으로 참조 된) 테이블을 받아야한다고 가정합니다. 그러나 단일 테이블에 대한 제약 조건 목록을 어떻게 실행할 수 있습니까?
  2. Table 클래스가 모두 자체 상태 (예 : 테이블 이름, 테이블 주석 등)와 제약 조건에 대한 링크를 저장하는 경우 이러한 단일 책임 원칙을 생각할 때 이러한 하나 또는 두 개의 "책임"입니까?
  3. 사례 2가 옳은 경우 논리 비즈니스 계층에서 링크를 관리하는 새 클래스를 만들어야합니까? 그렇다면 1. 더 이상 관련이 없습니다.
  4. "createStatement"메소드가 Table / Constraint 클래스의 일부 여야합니까, 아니면 제거해야합니까? 그렇다면 어디로 가야합니까? 각 데이터 스토리지 클래스 당 하나의 Manager 클래스 (예 : Table, Constraint, ...)? 아니면 링크 당 관리자 클래스를 만드십시오 (3과 유사)?

이 질문 중 하나에 답하려고 할 때마다 나는 어딘가에서 서클에서 달리고 있습니다.

열, 인덱스 등을 포함하면 문제가 훨씬 더 복잡해 지지만 간단한 테이블 / 제약 문제로 나를 도울 수 있다면 나머지는 스스로 해결할 수 있습니다.


3
어떤 언어를 사용하고 있습니까? 최소한 일부 스켈레톤 코드를 게시 할 수 있습니까? 실제 코드를 보지 않고 코드 품질과 가능한 리팩토링에 대해 논의하기는 매우 어렵습니다.
Péter Török

저는 C ++을 사용하고 있지만 어떤 언어로든이 문제가 발생할 수 있으므로 토론에서 벗어나려고했습니다.
Tim Meyer

예, 그러나 패턴 및 리팩토링의 적용은 언어에 따라 다릅니다. 예를 들어 @ back2dos는 아래 답변에서 AOP를 제안했지만 분명히 C ++에는 적용되지 않습니다.
Péter Török

SOLID 원칙에 대한 자세한 내용은 programmers.stackexchange.com/questions/155852/… 를 참조하십시오
LCJ

답변:


8

여기에서 "단일 책임 원칙"을 적용하기 위해 다른 관점에서 시작할 수 있습니다. 당신이 우리에게 보여준 것은 단지 당신의 어플리케이션의 데이터 모델입니다. 여기서 SRP는 다음을 의미합니다. 데이터 모델이 데이터 유지에만 책임이 있는지 확인하십시오.

당신이 당신의 XML 파일을 읽어가는 때, 그것에서 데이터 모델을 생성하고 SQL을 작성, 당신이해야 하지 하는 일은 당신에 아무것도 구현입니다 TableXML 또는 SQL 특정 클래스를. 데이터 흐름이 다음과 같이 보이기를 원합니다.

[XML] -> ("Read XML") -> [Data model of DB definition] -> ("Write SQL") -> [SQL]

따라서 XML 특정 코드를 배치해야하는 유일한 위치는 예를 들어라는 클래스 Read_XML입니다. SQL 특정 코드의 유일한 위치는와 같은 클래스 여야합니다 Write_SQL. 물론,이 두 가지 작업을 더 많은 하위 작업으로 분류하고 (클래스를 여러 관리자 클래스로 분할) "데이터 모델"은 해당 계층에서 책임을지지 않습니다. 따라서 createStatement데이터 모델 클래스에 추가하지 마십시오 . SQL에 대한 데이터 모델 책임이 제공되기 때문입니다.

테이블이 모든 부분 (이름, 열, 주석, 제약 조건 ...)을 보유해야한다고 설명 할 때 아무런 문제가 없습니다. 즉, 데이터 모델의 개념입니다. 그러나 "표"는 일부 부품의 메모리 관리도 담당합니다. 그것은 C ++ 관련 문제이며 Java 또는 C #과 같은 언어에서는 쉽게 직면하지 않습니다. 이러한 책임을 제거하는 C ++ 방식은 스마트 포인터를 사용하여 소유권을 다른 계층 (예 : 부스트 라이브러리 또는 자체 "스마트"포인터 계층)에 위임합니다. 그러나 순환 종속성은 일부 스마트 포인터 구현을 "자극 화"할 수 있습니다.

SOLID에 대한 추가 정보 : 여기 좋은 기사가 있습니다.

http://cre8ivethought.com/blog/2011/08/23/software-development-is-not-a-jenga-game

작은 예를 통해 SOLID를 설명합니다. 당신의 경우에 적용 해 봅시다 :

  • 당신은 수업뿐만 아니라 필요 Read_XML하고 Write_SQL, 또한 이들 2 종류의 상호 작용을 관리하는 세 번째 클래스를. 그것을이라고 부를 수 있습니다 ConversionManager.

  • DI 원리를 적용하면 여기에 의미 할 수있다 : ConversionManager는 인스턴스 작성해서는 안 Read_XMLWrite_SQL자체를. 대신 생성자를 통해 해당 개체를 주입 할 수 있습니다. 그리고 생성자는 이와 같은 서명을 가져야합니다

    ConversionManager(IDataModelReader reader, IDataModelWriter writer)

어디 IDataModelReader에서 Read_XML상속 되는 인터페이스이며 ,와 IDataModelWriter동일합니다 Write_SQL. 이렇게하면 ConversionManager확장 할 필요없이 확장 기능을 사용할 수 있습니다 (다른 독자 나 작성자에게 매우 쉽게 제공). 따라서 개방 / 종료 원칙에 대한 예가 있습니다. 다른 데이터베이스 공급 업체를 지원하려고 할 때 변경해야 할 사항에 대해 생각해보십시오. 이상적으로는 데이터 모델의 내용을 변경할 필요가 없으며 대신 다른 SQL-Writer를 제공하면됩니다.


이것은 SOLID를 매우 합리적으로 수행하는 것이지만 (공백 된) 상당히 빈약 한 데이터 모델에 대해 게터와 세터를 요구하여 "오래된 학교 Kay / Holub OOP"를 위반한다는 점에 유의하십시오. 또한 악명 높은 Steve Yegge rant 도 생각 나게합니다 .
user949300

2

이 경우 S의 SOLID를 적용해야합니다.

테이블에는 테이블에 정의 된 모든 제약 조건이 있습니다. 제약 조건은 참조하는 모든 테이블을 보유합니다. 평범하고 간단한 모델.

당신이 고수하는 것은 역 조회를 수행하는 능력, 즉 어떤 테이블이 어떤 제약 조건을 참조하는지 파악하는 능력입니다.
실제로 원하는 것은 인덱싱 서비스입니다. 이는 완전히 다른 작업이므로 다른 개체로 수행해야합니다.

매우 간단한 버전으로 분류하려면 :

class Table {
      void addConstraint(Constraint constraint) { ... }
      bool removeConstraint(Constraint constraint) { ... }
      Iterator<Constraint> getConstraints() { ... }
}
class Constraint {
      //actually I am not so sure these two should be exposed directly at all
      void addReference(Table to) { ... }
      bool removeReference(Table to) { ... }
      Iterator<Table> getReferencedTables() { ... }
}
class Database {
      void addTable(Table table) { ... }
      bool removeTable(Table table) { ... }
      Iterator<Table> getTables() { ... }
}
class Index {
      Iterator<Constraint> getConstraintsReferencing(Table target) { ... }
}

인덱스 구현에 대해서는 3 가지 방법이 있습니다.

  • getContraintsReferencing메소드는 실제로 인스턴스 전체 DatabaseTable크롤링하고 Constraint결과를 얻기 위해 s를 크롤링 할 수 있습니다. 비용이 얼마나 비싸고 얼마나 자주 필요한지에 따라 옵션이 될 수 있습니다.
  • 캐시를 사용할 수도 있습니다. 일단 정의 된 데이터베이스 모델이 변경 될 수있는 경우, 해당 인스턴스 TableConstraint인스턴스가 변경 될 때 신호를 발생시켜 캐시를 유지할 수 있습니다. 약간 더 간단한 해결책은 작업 Index할 전체의 "스냅 샷 인덱스"를 작성하여 Database버리는 것입니다. 응용 프로그램이 "모델링 시간"과 "쿼리 시간"을 크게 구분하는 경우에만 가능합니다. 이 두 가지를 동시에 수행 할 가능성이 높으면 이것이 불가능합니다.
  • 다른 옵션은 AOP 를 사용 하여 전체 작성 호출을 인터셉트하고 이에 따라 인덱스를 유지 보수하는 것입니다.

매우 자세한 답변은 지금까지의 솔루션을 좋아합니다! Table 클래스에 대해 DI를 수행하여 생성 중에 제약 조건 목록을 제공하면 어떻게 생각하십니까? 어쨌든 TableParser 클래스가 있습니다.이 클래스는 팩토리 역할을하거나 팩토리와 함께 작동 할 수 있습니다.
Tim Meyer

@Tim Meyer : DI는 반드시 생성자 주입 일 필요는 없습니다. DI는 멤버 기능으로도 수행 할 수 있습니다. 생성자를 통해 테이블의 모든 부품을 가져와야하는 경우 생성시에만 해당 부품을 추가하고 나중에 변경하지 않거나 단계별로 테이블을 생성 하려는지 여부에 따라 달라집니다. 그것이 디자인 결정의 기초가되어야합니다.
Doc Brown

1

순환 종속성의 치료법은 절대로 절대로 만들지 않을 것이라고 맹세합니다. 코딩 테스트 우선이 강력한 억제력이라는 것을 알았습니다.

어쨌든 순환 종속은 항상 추상 기본 클래스를 도입하여 깨질 수 있습니다. 이것은 그래프 표현에 일반적입니다. 여기서 테이블은 노드이고 외래 키 제약 조건은 가장자리입니다. 따라서 추상 테이블 클래스와 추상 제약 클래스 및 추상 열 클래스를 만듭니다. 그러면 모든 구현이 추상 클래스에 의존 할 수 있습니다. 이것은 최선의 표현은 아니지만 상호 연결된 클래스보다 개선 된 것입니다.

그러나이 문제에 대한 최상의 솔루션은 개체 관계를 추적하지 않아도 될 수 있습니다. XML을 SQL로만 변환하려는 경우 제약 조건 그래프의 메모리 내 표현이 필요하지 않습니다. 그래프 알고리즘을 실행하려면 제약 조건 그래프가 좋을 것이지만 언급하지 않았으므로 요구 사항이 아니라고 가정합니다. 지원하려는 각 SQL 언어에 대한 테이블 목록, 제약 조건 목록 및 방문자 만 있으면됩니다. 테이블을 생성 한 다음 테이블 외부의 구속 조건을 생성하십시오. 요구 사항이 변경 될 때까지 SQL 생성기를 XML DOM에 연결하는 데 아무런 문제가 없습니다. 내일을 위해 내일을 구하십시오.


이곳은 "(실제로는 그 이상이지만 단순하게 유지하자")가 등장하는 곳입니다. 예를 들어 테이블을 삭제해야하는 경우가 있으므로이 테이블을 참조하는 제약 조건이 있는지 확인해야합니다.
Tim Meyer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.