2015-01-23 2 views
10

솔리드 교장에 대한 연구를하고 있으며 저장소 패턴의 구현에 몇 가지 문제점을 발견했습니다. 나는 각 문제를 설명 할 것이고, 내가 틀렸다면 나를 바로 잡으십시오.리포지토리 패턴은 SOLID 원칙을 준수합니까?

문제는 1

저장소 패턴은 단일 책임 원칙

하자 우리가

public interface IRepository<T> where T: IEntity 
{ 

    IEnumerable<T> List { get; } 
    void Add(T entity); 
    void Delete(T entity); 
    void Update(T entity); 
    T FindById(int Id); 
} 

로 정의한 인터페이스를 가지고 있다고 (이 S)를 명확하게 위반 나누기 단일 책임 원리는 w 우리는이 인터페이스를 구현합니다. 하나의 클래스에서 우리는 Command와 Query를 둘 다 넣고 있습니다. 그리고 이것은 예상되지 않습니다.

문제 2

저장소 패턴 인터페이스 분리의 원칙 (I)

우리는 위의 인터페이스의이 구현이 말을 나누기.

첫 번째 구현

CustomerRepository : IRepository<Customer> 
{ 
    //All Implementation 
} 

두 번째 구현

ProductRepository : IRepository<Product> 
{ 
    //All Implementation except Delete Method. So Delete Method Will be 
    void Delete (Product product){ 
     throw Not Implement Exception! 
    } 
} 

그리고 ISP에 따라 "어떤 클라이언트가 사용하지 않는 방법에 의존하도록 강요해서는 안된다." 그래서 우리는 ISP에 위배된다는 것을 분명히 알았습니다.

제 이해는 저장소 패턴이 SOLID 원칙을 따르지 않는다는 것입니다. 어떻게 생각해? 교장을 위반하는 패턴의 유형을 선택해야하는 이유는 무엇입니까? 너의 의견이 필요해.

답변

21

분명히이 인터페이스를 구현할 때 단일 클래스에서 Command와 Query를 모두 사용하기 때문에이 인터페이스는 단일 책임 원칙을 위반합니다. 그리고 이것은 예상되지 않습니다.

이것은 단일 책임 원칙의 의미가 아닙니다. SRP 란 클래스가 이 가장 중요하다는 것을 의미합니다. 저장소의 주된 관심사는 "도메인 객체에 액세스하기 위해 콜렉션과 같은 인터페이스를 사용하여 도메인과 데이터 매핑 계층을 조정하는 것"(Fowler)입니다. 그것이 바로이 수업이하는 것입니다.

저장소 패턴은 당신을 귀찮게한다면, 단순히 당신이 구현하지 않을거야 방법을 포함하지 않는 다른 인터페이스를 제공하는 인터페이스 분리의 원칙을

를 나누기. 나는 개인적으로 그렇게하지 않을 것이다. API는 불필요하게 API를 혼란스럽게합니다.NotImplementedException은 매우 자명 한 입니다.

예외가있는 컴퓨팅의 규칙, 법률 또는 원칙이 많이 있으며 그 중 일부는 크게 잘못되었음을 알게 될 것입니다. 모호성을 포용하고,보다 실질적인 관점에서 소프트웨어를 작성하는 방법을 배우고, 절대적인 관점에서 소프트웨어 설계에 대해 생각하지 마십시오.

+0

고맙습니다 @ 로버트, 매우 도움이됩니다. –

+0

@AnkitSarkar'IRepository '은 사용하지 않을 때는 자체적으로 안티 패턴입니다. http://codebetter.com/gregyoung/2009/01/16/ddd-the-generic-repository/ – plalx

4

분명히 그것은 단일 책임 원칙 당신이 SRP가 무엇인지 매우 좁은 정의를 경우에만 분명

을 위반합니다. 사실 SOLID SOLID를 위반합니다. 원칙 자체는 모순됩니다. SRP는 DRY와 확연한 관계가 있습니다. 왜냐하면 종종 문제를 적절하게 분리하기 위해 반복해야하기 때문입니다. 어떤 상황에서는 LSP가 ISP와 경쟁하지 않습니다. OCP는 종종 DRY 및 SRP와 충돌합니다. 이 원칙들은 여기서 어렵고 빠른 규칙은 아니지만 당신을 인도하기 위해 ... 그들을 준수하려고 노력하지만, 깨뜨릴 수없는 법으로 취급하지 마십시오.

그 중에서도 Repository 아키텍처 패턴과 매우 구체적인 Generic Repository 구현 패턴이 혼동 스럽습니다. 일반 리포지토리는 구체적인 리포지토리와 다릅니다. 또한 저장소에 사용자가 언급 한 방법을 구현해야한다는 요구 사항도 없습니다.

예. 명령과 쿼리를 별도의 두 가지 우려 사항으로 구분할 수 있지만 각 책임을 다할 필요는 없습니다. 커맨드 쿼리 분리 (Command Query Seperation)는 좋은 원칙이지만 SOLID에 의해 다루어지는 것은 아닙니다. 그리고 관심사들을 분리시키는 것이 다른 책임들의 우선 순위에 속하는지 아닌지에 대한 공감대는 없습니다. 그들은 같은 책임의 다른 측면과 같습니다. Updating이 Deleting과 다른 책임이거나 id에 의한 질의가 유형이나 다른 것에 의한 질의와 다른 책임이 있다고 주장하고 싶다면 이것을 우스운 수준으로 가져갈 수 있습니다. 어떤 시점에서 선과 상자를 그려야하며, 대부분의 사람들은 "실체를 읽고 쓰는 것"은 하나의 책임입니다.

저장소 패턴 인터페이스 분리의 원칙을 인터페이스 독방 원리와

먼저 혼동 Liskov 교체 교장을 나누기. LSP는 귀하의 모범에 의해 침해당한 것입니다.

앞서 말했듯이, 저장소는 "컬렉션과 같은 인터페이스"가 아닌 특정 방법 세트를 구현할 필요가 없습니다. 그것은 내가 확실히 단지 구현하지 않을 오히려 바보 구현 및 하나 있지만,

지금
public interface IRepository<T> where...[...] {IEnumerable<T> List { get; }} 
public interface CustRepository : IRepository<Customer>, IRepoAdd, IRepoUpdate, IRepoDelete, IRepoFind {} 

가 선택적으로 LSP를 파괴하지 않고 다른 구성원 중 하나를 구현할 수 있습니다 : 사실,이처럼 구현 완벽하게 허용 될 수 LSP를 깨는 것을 피하십시오.

사실, 삭제하지 않고 저장소를 원하는 이유는 분명합니다. 내가 생각할 수있는 유일한 이유는 읽기 전용 저장소를 사용하기위한 별도의 인터페이스를 정의하는 읽기 전용 저장소 일 것입니다.

2

나는 저장소 패턴을 직접 사용하고 패턴을 사용하여 필요한 모든 인터페이스가 구현되었는지 확인했습니다. 이를 위해 모든 작업 (IEntityCreator, IEntityReader, IEntityUpdater, IEntityRemover)에 대해 별도의 인터페이스를 만들고 repostiory가 이러한 모든 인터페이스를 상속 받도록했습니다. 이 방법으로 모든 메소드를 구체적인 클래스에 구현할 수 있으며 모든 인터페이스를 개별적으로 사용할 수 있습니다. 저장소 패턴이 고체 원칙을 위반한다고 언급 할 이유는 없습니다.저장소의 '책임'을 올바르게 정의하면됩니다. 저장소의 책임은 T 유형의 데이터에 대한 모든 액세스를 용이하게하는 것입니다. 그게 전부입니다. 위에서 언급 한 바와 같이 ReferenceRepository<T>이라는 읽기 전용 저장소 인터페이스도 있으며 여기에는 IEntityReader<T> 인터페이스 만 포함됩니다. 모든 인터페이스는 빠른 복사를 위해 아래에 정의되어 있습니다. 그 외에 캐싱 및/또는 로깅을 포함한 몇 가지 구체적인 클래스도 만들었습니다. 이것은 I에 명시된 추가 작업을 SOLID에 통합하는 것입니다. 유형 IEntity은 엔티티 만 허용하고 다른 종류의 오브젝트는 허용하지 않는 마커 인터페이스로 사용됩니다 (어딘가에서 시작해야 함).

/// <summary> 
/// This interface defines all properties and methods common to all Entity Creators. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityCreator<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Create a new instance of <see cref="TEntity"/> 
    /// </summary> 
    /// <returns></returns> 
    TEntity Create(); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity Readers. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityReader<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Get all entities in the data store. 
    /// </summary> 
    /// <returns></returns> 
    IEnumerable<TEntity> GetAll(); 

    /// <summary> 
    /// Find all entities that match the expression 
    /// </summary> 
    /// <param name="whereExpression">exprssion used to filter the results.</param> 
    /// <returns></returns> 
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> whereExpression); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity Updaters. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityUpdater<TEntity> 
    where TEntity : IEntity 
{ 
    #region Methods 
    /// <summary> 
    /// Save an entity in the data store 
    /// </summary> 
    /// <param name="entity">The entity to save</param> 
    void Save(TEntity entity); 
    #endregion 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Entity removers. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IEntityRemover<TEntity> 
    where TEntity : IEntity 
{ 
    /// <summary> 
    /// Delete an entity from the data store. 
    /// </summary> 
    /// <param name="entity">The entity to delete</param> 
    void Delete(TEntity entity); 

    /// <summary> 
    /// Deletes all entities that match the specified where expression. 
    /// </summary> 
    /// <param name="whereExpression">The where expression.</param> 
    void Delete(Expression<Func<TEntity, bool>> whereExpression); 
} 

/// <summary> 
/// This interface defines all properties and methods common to all Repositories. 
/// </summary> 
public interface IRepository { } 

/// <summary> 
/// This interface defines all properties and methods common to all Read-Only repositories. 
/// </summary> 
/// <typeparam name="TEntity">The type of the entity.</typeparam> 
public interface IReferenceRepository<TEntity> : IRepository, IEntityReader<TEntity> 
    where TEntity : IEntity 
{ 

} 

/// <summary> 
/// This interface defines all properties and methods common to all Read-Write Repositories. 
/// </summary> 
public interface IRepository<TEntity> : IReferenceRepository<TEntity>, IEntityCreator<TEntity>, 
    IEntityUpdater<TEntity>, IEntityRemover<TEntity> 
    where TEntity : IEntity 
{ 

} 
+0

추신 : 비즈니스 엔티티 (ID가있는 엔티티)의 상속 된 인터페이스에 다른 기능을 추가했지만 좀 더 일반적인 솔루션을 만들기 위해이 인터페이스를 남겼습니다. –

관련 문제