GRASP 패턴
GRASPGeneral Responsibility Assignment Software Patterns (또는 일반 책임 할당 소프트웨어 패턴)은 객체지향 설계에서 객체의 책임을 적절히 분배하는 데 도움을 주는 설계 원칙입니다. GRASP 패턴은 소프트웨어 설계 시 객체 간 책임을 어떻게 배분할지, 객체가 어떤 역할을 수행해야 하는지를 명확하게 정의하는 데 중점을 둡니다. 이를 통해 설계의 유연성을 높이고, 유지보수성을 개선하며, 객체지향 원칙을 따르는 구조를 형성할 수 있습니다.
GRASP 패턴의 9가지 원칙
GRASP 패턴은 9가지 주요 설계 원칙으로 이루어져 있습니다. 각 원칙은 객체의 책임을 어떻게 할당해야 할지에 대한 지침을 제공합니다.
정보 전문가
정보 전문가Information Expert 패턴은 객체가 자신의 데이터를 책임지고 처리해야 한다는 원칙입니다. 객체가 자신의 상태를 변경하거나 데이터를 처리하는 책임을 가질 때, 데이터와 관련된 기능이 자연스럽게 그 객체에 할당됩니다.
public class Book
{
public string Title { get; }
public bool IsAvailable { get; private set; }
public Book(string title)
{
Title = title;
IsAvailable = true;
}
public void Borrow()
{
if (!IsAvailable)
throw new InvalidOperationException("The book is already borrowed.");
IsAvailable = false;
}
public void Return()
{
IsAvailable = true;
}
}
Book
객체는 대출 상태를 스스로 관리합니다. 도서 대출과 반납은Book
객체가 자신의 상태를 가장 잘 알고 있기 때문에 이 책임이 할당되었습니다.
창조자
창조자Creator 패턴은 한 객체가 다른 객체를 생성할 책임이 있을 때 그 관계를 명확히 정의합니다. 일반적으로 A 객체가 B 객체의 생성을 책임져야 하는 상황은 A 객체가 B 객체를 포함하거나, B 객체와 긴밀한 관계가 있을 때 발생합니다.
public class User
{
private List<BookBorrowing> _borrowedBooks = new();
public void BorrowBook(Book book)
{
var borrowing = new BookBorrowing(this, book);
_borrowedBooks.Add(borrowing);
}
}
User
객체는 도서를 대출할 때BookBorrowing
객체를 생성하는 책임을 가집니다.User
와BookBorrowing
사이의 강한 관계로 인해 창조자 패턴이 적용되었습니다.
제어자
제어자Controller 패턴은 시스템의 이벤트나 사용자의 요청을 처리할 책임이 있는 객체를 지정합니다. 제어자는 일반적으로 UI나 API와 같은 인터페이스에서 발생하는 이벤트를 처리하고, 해당 이벤트에 따른 처리를 조정합니다.
public class LibraryController
{
private readonly BorrowService _borrowService;
public LibraryController(BorrowService borrowService)
{
_borrowService = borrowService;
}
public void HandleBorrowRequest(int bookId)
{
_borrowService.BorrowBook(bookId);
}
}
LibraryController
는 외부에서 들어오는 대출 요청을 처리하고, 그 책임을BorrowService
에 위임하는 제어자 역할을 수행합니다.
저수준 결합
저수준 결합Low Coupling 패턴은 객체 간 결합도를 최소화하는 설계 원칙입니다. 객체들 간의 상호 의존성을 줄여, 하나의 객체가 변경되더라도 다른 객체에 미치는 영향을 최소화합니다.
public interface IBookRepository
{
Book FindById(int id);
}
public class BookRepository : IBookRepository
{
public Book FindById(int id)
{
// 데이터베이스에서 책을 조회하는 로직
}
}
public class LibraryService
{
private readonly IBookRepository _bookRepository;
public LibraryService(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
public void BorrowBook(int bookId)
{
var book = _bookRepository.FindById(bookId);
book.Borrow();
}
}
LibraryService
는IBookRepository
인터페이스에 의존하며, 이를 통해 실제 구현에 결합되지 않도록 설계되었습니다. 이는 저수준 결합 원칙을 잘 반영한 예입니다.
고수준 응집
고수준 응집High Cohesion은 객체가 수행하는 책임들이 밀접하게 연관된 작업들로 구성되도록 하여, 객체가 단일한 목적을 수행하게 만드는 원칙입니다. 응집도가 높으면 객체는 책임이 명확하고 변경에 강합니다.
public class BorrowService
{
private readonly IBookRepository _bookRepository;
public BorrowService(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
public void BorrowBook(int bookId)
{
var book = _bookRepository.FindById(bookId);
book.Borrow();
}
public void ReturnBook(int bookId)
{
var book = _bookRepository.FindById(bookId);
book.Return();
}
}
BorrowService
는 대출과 반납이라는 밀접한 책임만을 수행하며, 응집도가 높은 서비스입니다.
순차 의존성
순차 의존성Indirection 패턴은 중간 객체를 사용하여 두 객체 간의 결합도를 낮추는 원칙입니다. 이를 통해 객체들 간의 직접적인 의존성을 줄일 수 있습니다.
public class LibraryService
{
private readonly IBookRepository _bookRepository;
public LibraryService(IBookRepository bookRepository)
{
_bookRepository = bookRepository;
}
public void AddBook(Book book)
{
_bookRepository.Save(book);
}
}
LibraryService
객체가BookRepository
와LibraryController
사이의 중간자 역할을 할 수 있습니다.
다형성
다형성Polymorphism 패턴은 객체들이 공통된 인터페이스나 부모 클래스를 통해 상호작용하도록 설계하는 원칙입니다. 이를 통해 다양한 객체가 동일한 메서드 호출에 서로 다른 방식으로 응답할 수 있습니다.
public interface IPrintable
{
void Print();
}
public class Book : IPrintable
{
public void Print() { /* Print book details */ }
}
public class Magazine : IPrintable
{
public void Print() { /* Print magazine details */ }
}
IPrintable
인터페이스를 사용하여Book
과Magazine
객체가 각각 다른 방식으로 정보를 출력하도록 할 수 있습니다.
순차적 책임 할당
순차적 책임 할당Pure Fabrication 패턴은 도메인 모델에 직접적으로 포함되지 않지만, 특정한 책임을 수행하기 위한 객체를 만드는 것을 말합니다. 이는 응집도를 높이고, 코드의 재사용성을 높이는 데 도움이 됩니다.
예시: LoggingService
는 도메인 모델과 직접적으로 관련이 없지만, 로깅 작업을 처리하기 위한 별도의 서비스로 설계될 수 있습니다.
변경 보호
변경 보호Protected Variations 패턴은 시스템의 특정 부분이 변경될 때, 그 변경이 다른 부분에 영향을 미치지 않도록 보호하는 원칙입니다. 이를 위해 인터페이스나 추상화를 통해 변경 가능성을 줄이는 방법을 사용합니다.
public interface IBookRepository
{
void Save(Book book);
}
IBookRepository
인터페이스를 통해 다양한 데이터 저장소를 지원할 수 있도록 설계합니다.
GRASP 패턴의 중요성
GRASP 패턴은 객체지향 설계의 기본 개념이나 원칙과 일부 중복되는 부분이 있지만, 이를 구체적인 책임 할당 방식으로 세분화하여 다루기 때문에 별도로 항목으로 분리합니다. 즉, GRASP 패턴은 객체지향의 일반적인 개념을 실무에서 어떻게 적용할지에 대한 구체적인 지침을 제공하는 역할을 합니다. 객체지향의 특징들이 일반적인 설계 원칙이라면, GRASP 패턴은 특정 상황에서 객체의 책임을 어떻게 할당할지에 대한 명확한 방법론을 제시하는 것이 차이점입니다. 예를 들어, 객체지향에서 다형성과 캡슐화 같은 개념이 중요한 원칙이라면, GRASP 패턴에서는 정보 전문가나 창조자 등의 패턴을 통해 어떤 객체가 책임을 가져야 하는지 구체적인 결정을 내리게 합니다. 결과적으로 객체들 간의 상호작용을 설계하는 데 중요한 원칙을 제공하는데, 이를 통해 시스템의 유지보수성과 확장성을 향상시킬 수 있으며, 결합도는 낮추고 응집도는 높여 시스템을 보다 견고하게 만들 수 있습니다.