'리파지터리'에 해당되는 글 1건

  1. 2010.09.25 Specification Pattern 설명과 구현의 이해

Specification Pattern 설명과 구현의 이해

Specification Pattern 은 DDD와 PoEAA에서 동시에 다루어지는 명세서 라는 패턴입니다. 도메인 모델은 다양한 종류의 조건 쿼리를 실행시키려 할 때  DAL( Data Access Layer ) 과 같은 프로바이더에 구현한 메소드를 호출할 필요성이 발생합니다.

실행조건마다 그 명세와 일치하는 메소드가 프로파이더에 작성되고, 도메인 오브젝트와 프로바이더는 점점 강하게 연결됩니다. 명세조건만 약간씩 다른 중복 코드들이 양산되게 되죠.

Specification Pattern 은 도메인 오브젝트가 일관성있는 인터페이스로 프로바이더를 이용하게 함으로써 커플링을 느슨하게 만들며, 정확한 명세가 무엇인지를 연결된 객체의 '코드' 가 아닌 모델 자체가 설명하도록 하면서 응집성과 SoC를 달성하도록 하는 패턴 입니다.


Specification Pattern 을 비유해 설명하자면 , 내가 필요한 책을 도서관의 서가마다 돌아다니며 찾는 것이 아니라, 책의 목록을 '사서'에게 전달하고 사서로 부터 책을 받기만 하면 도서관 마다의 대출 방법과 서가의 위치 및 구조를  일일이 알고 있지 않더라도 목적을 달성 할수 있다는 아이디어를 구현하는 패턴 입니다. 책의 목록이 Specification 이며,  사서가 프로바이더의 인터페이스입니다. 나는 도서관의 구조를 알필요가 없이 목록규칙(Criteria)만 지키면 되고, 나중에 동일한 책을 빌리고 싶다면 이전에 작성해 놓은 목록을 사서에게 건네 주기만 하면 됩니다.


명세는 도메인 모델에 구현하며, 프로바이더에 Specification 인터페이스에 기준( Criteria )을 구현한 인스턴스를 전달함으로써 목적을 달성합니다.

ISpecificaton 인터페이스와 쿼리 조합 클래스 구현 다이어그램.



다른 여러 패턴에 대해 우리는 "왜 이리 공부해야 할 패턴이 많아~ 아휴 이 패턴은 그냥 넘어 갈까" 하고 지나치는 일이 많습니다. Specification Pattern 은 그 가치를 알기 전까지 가장 간과하기 쉬운 패턴 일 듯 합니다.

Specification Pattern 의 개념과 구현 설명이 매우 잘 되어 있는 블로그 포스트가 있어 번역 포스트 합니다.


번역 :

The Specification Pattern: A Primer
Posted March 25th 2005 by Matt Berther

Specification Pattern 은 클래스의 인터페이스에서 잡동사니들을 제거해주는 동시에 확장성을 증가시켜주고 커플링을 낮춰주는  매우 강력한 디자인 패턴입니다.

이것의 주된 쓰임은 어떤 기준에 기반한 개체의 부분집합을 선택하는 것과 다양한 상황에서 선택을 리프레쉬 시키는 것입니다.

예를 들어, 나는 이와 비슷한 것으로 보이는 인터페이스를 가진 클래스를 매우 많이 봤습니다.


public class User
{
    public string Company;
    public string Name;
    public string City;
}

public class UserProvider
{
    public User[] GetUserByName(string name)
    {
    }

    public User[] GetUsersByCity(string name)
    {
    }

    public User[] GetUsersByCompany(string company)
    {
    }
}


이 모델을 이용함으로써, 사용자검색을 위한 새로운 조건을 부여하고 싶을 때마다 유저프로바이더 클래스에 메소드를 추가해야 하고, 이는 인터페이스를 혼란스럽게 하는 것을 알

수 있습니다.

이제 specification pattern을 사용한 똑같은 사례를 살펴봅시다.


public class User
{
    public string Company;
    public string Name;
    public string City;
}



public class UserSpecification
{
    public virtual bool IsSatisfiedBy(User user)
    {
        return true;
    }
}

public class UserProvider
{
    public User[] GetBySpecification(UserSpecification spec)
    {
        ArrayList list = new ArrayList();

        UserCollection coll = SomeMethodToPopulateTheUserCollection();
        foreach (User user in coll)
        {
            if (spec.IsSatisfiedBy(user))
            {
                list.Add(user);
            }
        }

        return (User[])list.ToArray(typeof(User));
    }
}

class UserCompanySpecification : UserSpecification
{
    private readonly string companyName;

    public UserCompanySpecification(string companyName)
    {
        this.companyName = companyName;
    }

    public override bool IsSatisfiedBy(User user)
    {
        return user.Company.Equals(companyName);
    }
}



이 패턴을 사용함으로써, 유저프로바이더 클래스로부터 특화된 모든 메소드들을 제거합니다. 

또한, 느슨한 커플링으로 인해  검색을 위한 부가적인 조건을 언제든 추가할 수 있습니다. 우리는 기존의 인터페이스를 오염시키기 보다는 새로운 UserSpecification 을 구현하고,

GetBySpecification  메소드에 이 인스턴스를 던져주기만 하면 됩니다.

이는 프로바이더 코드가 사용자가 원하는 방법을 알고 있다고 가정하기보다는 호출하는 코드가 어떤 컬렉션이든 어떻게 필터할 것인지 정확히 결정하도록 해줍니다.

물론, API 디자이너가 소수의 잘쓰이는 Specification을 API 그 자체에  집어넣는 것은 허용됩니다.

이 패턴은 매우 강력하지만, 다른 것처럼 과용될 수 있습니다. 이 패턴을 쓰거나 쓰지 말아야 할 때에 대한 연결된 설명을 고려하여 결과를 리뷰해보는 것을 명심하십시오. 


 

신고

이 글을 Twitter / Facebook 에 공유하기
이 글이 유익하다면 아래의 트위터 버튼을 눌러 공유해 주시거나, 페이스북 "좋아요" 버튼을 눌러 주세요.

   


Posted by 반더빌트


티스토리 툴바