자체 메소드 또는 다른 클래스를 통해 오브젝트를 저장 하시겠습니까?


38

객체를 저장하고 검색하려면 처리 할 다른 클래스를 만들어야합니까, 아니면 클래스 자체에서 처리하는 것이 더 낫습니까? 아니면 둘 다 섞을까요?

OOD 패러다임에 따라 권장되는 것은 무엇입니까?

예를 들어

Class Student
{
    public string Name {set; get;}
    ....
    public bool Save()
    {
        SqlConnection con = ...
        // Save the class in the db
    }
    public bool Retrieve()
    {
         // search the db for the student and fill the attributes
    }
    public List<Student> RetrieveAllStudents()
    {
         // this is such a method I have most problem with it 
         // that an object returns an array of objects of its own class!
    }
}

대. (나는 다음이 권장된다는 것을 알고 있지만, Student수업 의 응집력에 대해 조금 나아 보인다 )

Class Student { /* */ }
Class DB {
  public bool AddStudent(Student s)
  {

  }
  public Student RetrieveStudent(Criteria)
  {
  } 
  public List<Student> RetrieveAllStudents()
  {
  }
}

섞는 건 어때요?

   Class Student
    {
        public string Name {set; get;}
        ....
        public bool Save()
        {
            /// do some business logic!
            db.AddStudent(this);
        }
        public bool Retrieve()
        {
             // build the criteria 
             db.RetrieveStudent(criteria);
             // fill the attributes
        }
    }


@gnat, 내가 더 잘되는 요구 생각하고 "장점과 단점"을 추가하고 제거 할 문제가 없을 기반의 의견이지만, 사실 나는 좋습니다 OOD 패러다임에 대한 의미
아마드

3
정답이 없습니다. 그것은 당신의 필요, 선호도, 수업이 어떻게 사용되는지 그리고 프로젝트가 어디로 가는지에 달려 있습니다. 유일한 올바른 OO의 것은 당신이 저장하고 개체로 검색 할 수 있습니다.
Dunk

답변:


34

단일 책임 원칙 , 우려의 분리기능적 응집력 . 이러한 개념을 읽으면 다음과 같은 대답을 얻을 수 있습니다 .

Student"DB"클래스 와 분리하는 (또는 StudentRepository보다 일반적인 규칙을 따르는) 간단한 이유 는 Student지속성에 책임이있는 코드에 영향을주지 않고 클래스에 존재하는 "비즈니스 규칙"을 변경 하고, 반대로.

이러한 종류의 분리는 비즈니스 규칙과 지속성뿐만 아니라 시스템의 많은 관심사 사이에서 매우 중요하여 관련없는 모듈에 미치는 영향을 최소화하면서 변경을 수행 할 수 있습니다 (때로는 피할 수 없기 때문에 최소화해야 함). 지속적인 변경이있을 때 유지 관리가 쉽고 안정적인 시스템을 더욱 강력하게 구축 할 수 있습니다.

첫 번째 예에서와 DB같이 단일 클래스 또는 의 종속성으로 비즈니스 규칙과 지속성을 함께 혼합 Student하면 두 가지 매우 다른 관심사를 결합하게됩니다. 그들은 함께 속해있는 것처럼 보일 수 있습니다. 그들은 동일한 데이터를 사용하기 때문에 응집력이있는 것처럼 보입니다. 그러나 여기에 문제가 있습니다. 응집력은 절차간에 공유되는 데이터만으로는 측정 할 수 없으며, 존재하는 추상화 수준도 고려해야합니다. 실제로 이상적인 응집력 유형은 다음과 같습니다.

기능적 응집력 은 모듈의 일부가 그룹화되어 모듈의 단일 정의 된 작업에 기여하기 때문에 발생합니다.

그리고 일정 Student기간 동안 유효성 검사를 수행해 도 "단일 정의 된 작업"이 형성되지 않습니다. 다시 한 번 비즈니스 규칙과 지속성 메커니즘은 시스템의 두 가지 매우 다른 측면으로, 우수한 객체 지향 디자인의 많은 원칙에 의해 분리되어야합니다.

나는에 대해 읽어 보시기 바랍니다 클린 건축 보고, 단일 책임 원칙에 대한이 이야기 (매우 비슷한 예를 사용), 시청 청소 아키텍처에 대한이 이야기 뿐만 아니라. 이러한 개념은 그러한 분리의 원인을 설명합니다.


11
아,하지만 Student수업은 제대로 캡슐화되어야합니다. 그러면 외부 클래스가 어떻게 개인 상태의 지속성을 관리 할 수 ​​있습니까? 캡슐화는 SoC 및 SRP와 균형을 이루어야하지만 신중하게 타협점을 계산하지 않고 단순히 다른 것을 선택하는 것은 잘못되었을 수 있습니다. 이 수수께끼에 대한 가능한 해결책은 지속 코드가 사용할 패키지 전용 접근자를 사용하는 것입니다.
amon

1
@ amon 나는 그것이 그렇게 간단하지 않다는 데 동의하지만 캡슐화가 아니라 결합의 문제라고 생각합니다. 예를 들어, 비즈니스에 필요한 모든 데이터에 대한 추상 메소드를 사용하여 Student 클래스를 abstract로 만들 수 있습니다. 이런 식으로 DB의 데이터 구조는 리포지토리 구현 뒤에 완전히 숨겨져 있으므로 Student 클래스에서도 캡슐화됩니다. 그러나 동시에 저장소는 Student 클래스와 밀접하게 연결되어 있습니다. 추상 메소드가 변경되면 구현도 변경되어야합니다. 그것은 절충에 관한 것입니다. :)
MichelHenrich

4
SRP를 통해 프로그램을보다 쉽게 ​​유지 관리 할 수 ​​있다는 주장은 모호합니다. SRP가 만드는 문제는 이름이 잘 알려진 클래스 모음을 갖는 대신 이름이 클래스가 할 수 있고 할 수없는 모든 것을 말해줍니다 (즉, 이해하기 쉬운)는 수백 / 사람들이 고유 한 이름을 선택하기 위해 동의어 사전을 사용해야하는 수천 개의 클래스, 많은 이름이 비슷하지만 그다지 다르지 않으며 모든 채프를 정리하는 것이 번거로운 일입니다. IOW, 전혀 유지 보수 할 수 없음, IMO.
Dunk

3
@ 덩크는 클래스가 사용 가능한 유일한 모듈화 단위 인 것처럼 말합니다. SRP에 이어 많은 프로젝트를 보았고 작성했으며 패키지와 라이브러리를 올바르게 사용하지 않는 경우에만 예제가 발생한다는 데 동의합니다. 책임을 패키지로 분리하면 컨텍스트가 암시되므로 클래스의 이름을 자유롭게 지정할 수 있습니다. 그런 다음 이와 같은 시스템을 유지 관리하려면 변경해야 할 클래스를 검색하기 전에 올바른 패키지로 드릴 익사하는 것만 있으면됩니다. :)
MichelHenrich

5
@Dunk OOD 원칙은 "원칙적으로 X, Y 및 Z로 인해 수행하는 것이 좋습니다."라고 말할 수 있습니다. 항상 적용해야한다는 의미는 아닙니다. 사람들은 실용적이고 트레이드 오프에 가중치를 두어야합니다. 큰 프로젝트에서 이점은 일반적으로 당신이 말하는 학습 곡선을 능가하는 이점입니다. 물론 그것은 대부분의 사람들에게 현실이 아니므로 그렇게 많이 적용해서는 안됩니다. 그러나 더 가벼운 응용 프로그램 SRP에서도 비즈니스 규칙에서 지속성을 분리하면 비용을 능가하는 이점이 항상 있음에 동의 할 수 있다고 생각합니다 (던지기 코드 제외).
MichelHenrich

10

두 가지 접근법 모두 단일 책임 원칙을 위반합니다. 첫 번째 버전은 Student클래스에게 많은 책임을 부여하고 특정 DB 액세스 기술에 연결합니다. 두 번째는 DB학생뿐만 아니라 프로그램의 다른 종류의 데이터 객체에 대한 책임이 있는 거대한 수업으로 이어집니다 . 편집 : 세 번째 접근 방식은 DB 클래스와 클래스 사이에 주기적 종속성을 생성하기 때문에 최악 Student입니다.

장난감 프로그램을 쓰지 않으려면 아무 것도 사용하지 마십시오. 대신 StudentRepository로드 및 저장을위한 API를 제공하기 위해 와 같은 다른 클래스를 사용 하십시오. CRUD 코드를 직접 구현한다고 가정하십시오. ORM 프레임 워크 사용을 고려할 수도 있습니다.이 프레임 워크는 어려운 작업을 수행 할 수 있습니다 (그리고 프레임 워크는 일반적으로로드 및 저장 조작이 필요한 위치에 일부 결정을 시행합니다).


고마워, 나는 대답을 수정했다. 어쨌든 여기서 요점은 뚜렷한 레이어를 만드는 것입니다
Ahmad

DB 대신 StudentRepository를 사용하도록 제안하는 것이 있습니다 (이유를 모르십시오! 아마도 단일 책임) 어쨌든 각 클래스마다 왜 다른 저장소를 얻을 수 없습니까? 단지 데이터 액세스 계층이라면 더 많은 정적 유틸리티 함수가 있고 함께 있지 않을 수 있습니까? 다만 어쩌면 그것은 그렇게 더러운되었고, (내 영어 죄송합니다) 클래스를 붐비는 것
아마드

1
@Ahmad : 몇 가지 이유와 고려해야 할 몇 가지 장단점이 있습니다. 이것은 전체 책을 채울 수 있습니다. 이러한 이유는 특히 수명주기가 길어질 때 프로그램의 테스트 가능성, 유지 관리 가능성 및 장기적인 발전 가능성으로 귀결됩니다.
Doc Brown

DB 기술에 중대한 변화가 있었던 프로젝트를 수행 한 사람이 있습니까? (대규모 다시 작성하지 않고?) 나는하지 않았다. 그렇다면 SRP를 따르는 것이 여기에 제안되어 해당 DB에 묶이지 않도록 크게 도움이 되었습니까?
user949300

@ user949300 : 나는 나 자신을 명확하게 표현하지 않았다고 생각합니다. 학생 수업은 특정 DB 기술에 묶이지 않고 특정 DB 액세스 기술에 묶여 있으므로 지속성을 위해 SQL DB와 같은 것을 기대합니다. Student 클래스를 지속성 메커니즘을 완전히 인식하지 못하면 다른 상황에서 클래스를 훨씬 쉽게 재사용 할 수 있습니다. 예를 들어, 테스트 컨텍스트 또는 오브젝트가 파일에 저장된 컨텍스트에서. 그리고 그것은 제가 과거에 정말로 자주 한 일입니다. 이를 통해 시스템의 전체 DB 스택을 변경할 필요가 없습니다.
Doc Brown

3

데이터 지속성에 사용할 수있는 패턴이 꽤 있습니다. 이 작업 단위 패턴이있다, 저장소 와에 따라서 원격 외관처럼 사용 될 수있는 몇 가지 추가 패턴이있다, 패턴.

대부분의 팬과 비평가가 있습니다. 종종 응용 프로그램에 가장 적합한 것으로 고르고 응용 프로그램을 고수합니다 (모든 장점과 단점이 있습니다. 즉, 두 패턴을 동시에 사용하지 않는 경우 ... 정확히 확신하지 않는 한).

참고로, 예제에서 RetrieveStudent, AddStudent는 정적 메소드 여야합니다 (인스턴스 종속적이지 않기 때문에).

클래스 내 저장 /로드 메소드를 사용하는 다른 방법은 다음과 같습니다.

class Animal{
    public string Name {get; set;}
    public void Save(){...}
    public static Animal Load(string name){....}
    public static string[] GetAllNames(){...} // if you just want to list them
    public static Animal[] GetAllAnimals(){...} // if you actually need to retrieve them all
}

개인적으로 나는 그러한 접근 방식을 상당히 작은 응용 프로그램, 개인적 용도를위한 도구 또는 객체를 저장하거나로드하는 것보다 더 복잡한 사용 사례가 없을 것이라고 안정적으로 예측할 수있는 곳에서만 사용합니다.

또한 개인적으로 작업 단위 패턴을 참조하십시오. 당신이 그것을 알게되면, 그것은 작은 경우와 큰 경우에 정말 좋습니다. 그리고 많은 프레임 워크 / API에서 EntityFramework 또는 RavenDB라는 이름을 지원합니다.


2
참고 사항 : 개념적으로 응용 프로그램이 실행되는 동안 하나의 DB 만 있지만 RetriveStudent 및 AddStudent 메서드를 정적으로 만들어야한다는 의미는 아닙니다. 리포지토리는 일반적으로 개발자가 나머지 응용 프로그램에 영향을주지 않고 구현을 전환 할 수 있도록 인터페이스로 만들어집니다. 예를 들어 다른 데이터베이스를 지원하여 응용 프로그램을 배포 할 수도 있습니다. 물론이 같은 논리는 UI와 같이 지속성 외에 다른 많은 영역에도 적용됩니다.
MichelHenrich

당신은 확실히 맞습니다, 나는 원래의 질문에 기초하여 많은 가정을했습니다.
Gerino

내가 가장 좋은 방법은 레이어를 사용하여 저장소 패턴에서 언급 한 것처럼 생각, 감사합니다
아마드

1

객체가 데이터 스토어에 연결되어 있고 비자에 대한 연결이 매우 간단한 앱인 경우 (즉, 데이터 스토어의 속성으로 생각할 수있는 경우) 해당 클래스에 대해 .save () 메서드를 사용하는 것이 좋습니다.

그러나 나는 그것이 매우 예외적이라고 생각합니다.

오히려 일반적으로 클래스가 데이터와 기능 (좋은 OO 시민과 같은)을 관리하게하고 지속 메커니즘을 다른 클래스 또는 클래스 세트로 외부화하는 것이 좋습니다.

다른 옵션은 주석과 같이 선언적으로 지속성을 정의하는 지속성 프레임 워크를 사용하는 것이지만 여전히 지속성을 외부화하고 있습니다.


-1
  • 해당 클래스에서 객체를 직렬화하는 메소드를 정의하고 데이터베이스 또는 파일 또는 기타에 넣을 수있는 바이트 시퀀스를 리턴합니다.
  • 입력과 같은 바이트 시퀀스를 취하는 해당 객체의 생성자를 갖습니다.

RetrieveAllStudents()방법에 관해서는 , 당신의 느낌이 맞습니다. 여러분의 독특한 학생들 목록이있을 수 있기 때문에 실제로 잘못되었을 수 있습니다. 왜 단순히 목록을 Student클래스 외부에 유지하지 않습니까?


Laravel과 같은 일부 PHP 프레임 워크에서 이러한 추세를 보았습니다. 이 모델은 데이터베이스에 연결되어 있으며 일련의 객체를 반환 할 수도 있습니다.
Ahmad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.