GRASP 패턴: Indirection (간접성)
Prof. Jong Min Lee이(가) 약 한달 전에 추가함
GRASP 패턴: Indirection (간접성) - 간단한 설명과 예시¶
핵심 아이디어: 두 객체 사이의 직접적인 결합을 피하기 위해 중간 객체를 도입하여 책임을 할당합니다. 이 중간 객체는 두 객체 사이의 통신을 중재하거나, 인터페이스 역할을 수행하여 결합도를 낮추고 유연성을 높입니다.
간단한 설명:
직접적인 결합은 한 객체의 변경이 다른 객체에 직접적인 영향을 미칠 수 있어 시스템의 유지보수성과 확장성을 저해합니다. 간접성 패턴은 이러한 직접적인 의존성을 줄이기 위해 중간에 또 다른 객체를 도입합니다. 이 중간 객체는 클라이언트와 서비스 제공자 사이의 계약(인터페이스)을 정의하거나, 요청을 처리하는 방식을 추상화하여 양쪽 객체의 변화에 대한 영향을 최소화합니다.
예시:
사용자 인터페이스(UI)와 데이터베이스 간의 직접적인 통신을 생각해 봅시다. UI 객체가 특정 데이터베이스 클래스에 직접적으로 의존하여 데이터를 저장하고 조회하는 경우, 데이터베이스 종류가 변경되면 UI 코드 전체를 수정해야 할 가능성이 큽니다. 이는 높은 결합도의 문제입니다.
간접성 패턴을 적용하여 이 문제를 해결할 수 있습니다. 데이터베이스와 UI 사이에 데이터 접근 객체 (Data Access Object, DAO) 라는 중간 객체를 도입하는 것입니다.
// 사용자 인터페이스 (UI) 클래스
class UserInterface {
private UserDAO userDAO; // 직접적인 데이터베이스 접근 대신 DAO에 의존
public UserInterface(UserDAO userDAO) {
this.userDAO = userDAO;
}
public User getUserDetails(String userId) {
return userDAO.getUserById(userId);
}
public void saveUser(User user) {
userDAO.saveUser(user);
}
}
// 데이터 접근 객체 (DAO) 인터페이스
interface UserDAO {
User getUserById(String userId);
void saveUser(User user);
// ... 다른 데이터 접근 관련 메서드 ...
}
// 구체적인 데이터베이스 접근 객체 (예: MySQL)
class MySQLUserDAO implements UserDAO {
private MySQLDatabaseConnection dbConnection;
public MySQLUserDAO(MySQLDatabaseConnection dbConnection) {
this.dbConnection = dbConnection;
}
@Override
public User getUserById(String userId) {
// MySQL 데이터베이스에서 사용자 정보 조회 로직
System.out.println("MySQL에서 사용자 " + userId + " 정보 조회");
return new User(userId, "John Doe"); // 예시
}
@Override
public void saveUser(User user) {
// MySQL 데이터베이스에 사용자 정보 저장 로직
System.out.println("MySQL에 사용자 " + user.getId() + " 정보 저장");
// ...
}
}
// 구체적인 데이터베이스 접근 객체 (예: Oracle)
class OracleUserDAO implements UserDAO {
private OracleDatabaseConnection dbConnection;
public OracleUserDAO(OracleDatabaseConnection dbConnection) {
this.dbConnection = dbConnection;
}
@Override
public User getUserById(String userId) {
// Oracle 데이터베이스에서 사용자 정보 조회 로직
System.out.println("Oracle에서 사용자 " + userId + " 정보 조회");
return new User(userId, "Jane Doe"); // 예시
}
@Override
public void saveUser(User user) {
// Oracle 데이터베이스에 사용자 정보 저장 로직
System.out.println("Oracle에 사용자 " + user.getId() + " 정보 저장");
// ...
}
}
// 도메인 객체
class User {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
// 데이터베이스 연결 클래스 (예시)
class MySQLDatabaseConnection { /* ... */ }
class OracleDatabaseConnection { /* ... */ }
public class Client {
public static void main(String[] args) {
// MySQL 데이터베이스 사용
UserDAO mysqlUserDAO = new MySQLUserDAO(new MySQLDatabaseConnection());
UserInterface uiWithMySQL = new UserInterface(mysqlUserDAO);
uiWithMySQL.getUserDetails("123");
uiWithMySQL.saveUser(new User("456", "Peter Pan"));
System.out.println("--- 데이터베이스 변경 ---");
// Oracle 데이터베이스로 변경 (UI 코드 변경 없음)
UserDAO oracleUserDAO = new OracleUserDAO(new OracleDatabaseConnection());
UserInterface uiWithOracle = new UserInterface(oracleUserDAO);
uiWithOracle.getUserDetails("789");
uiWithOracle.saveUser(new User("012", "Alice"));
}
}
이 예시에서 UserDAO
인터페이스가 간접적인 역할을 수행합니다.
UserInterface
는 구체적인 데이터베이스 클래스 (MySQLDatabaseConnection
,OracleDatabaseConnection
)에 직접적으로 의존하는 대신,UserDAO
라는 인터페이스에 의존합니다.UserDAO
인터페이스는 데이터 접근에 필요한 기본적인 동작 (getUserById
,saveUser
)을 정의합니다.MySQLUserDAO
와OracleUserDAO
는UserDAO
인터페이스를 구현하여 각각 MySQL과 Oracle 데이터베이스와의 통신을 처리하는 구체적인 방법을 제공합니다.
간접성의 장점:
- 낮은 결합도:
UserInterface
는 특정 데이터베이스 구현체에 대한 지식이 없으므로 결합도가 낮아집니다. 데이터베이스가 변경되더라도UserInterface
코드를 수정할 필요가 거의 없습니다. - 유연성: 시스템을 다양한 데이터베이스 환경에 쉽게 적용할 수 있습니다. 새로운 데이터베이스를 지원해야 할 경우, 해당 데이터베이스를 위한 새로운
UserDAO
구현체만 만들면 됩니다. - 재사용성:
UserDAO
인터페이스는 다양한 UI 컴포넌트에서 재사용될 수 있습니다. - 테스트 용이성: 인터페이스를 기반으로 Mock 객체를 만들어 UI 로직을 데이터베이스 의존성 없이 독립적으로 테스트할 수 있습니다.
결론적으로, 간접성 패턴은 중간 객체를 도입하여 객체 간의 직접적인 결합을 줄임으로써 시스템의 유연성, 확장성, 유지보수성 및 테스트 용이성을 향상시키는 중요한 설계 원칙입니다. DAO 패턴 외에도 Mediator, Observer 등 다양한 디자인 패턴에서 간접성 개념이 활용됩니다.