본문 바로가기

코딩/Software Architecture

[Design Pattern] GRASP Pattern

* GRASP Pattern

General Responsibility Assignment Software Patterns의 약자로,

Object-Orinted-Programing의 핵심중 하나인 객체에 책임을 부여하는 원칙을 기술한 패턴.

총 9가지 원칙을 가지고 있다.

 

1. Information Expert : 정보 은닉의 원칙

 - 데이터를 가지고 있는 객체에 책임을 부여하라. (getter 함수를 덜 사용하게 되겠다..)

 - 객체는 데이터와 처리 로직이 함께 묶여 있어야 한다.

 - 정보 은닉을 통해 자신의 데이터를 감추고, 오직 Method로만 데이터를 처리하고, 외부에는 그 기능(책임)만 제공한다.

 

2. Creator : 객체 생성의 원칙

 - 객체의 생성은 '생성될 객체'의 컨텍스트를 알고 있는 다른 객체가 있다면, 알고 있는 객체에 책임 부여.

 - ex) A, B 객체의 관계가 아래 중 하나라면 A의 생성을 B의 책임으로 부여.

    - B 객체가 A 객체를 포함

    - B 객체가 A 객체의 정보를 기록하고 있음

    - A 객체가 B 객체의 일부

    - B 객체가 A 객체를 긴밀하게 사용

    - B 객체가 A 객체의 생성에 필요한 정보를 가지고 있음.

Factory Pattern 참고?

글로만 봐서는 이해가 조금 힘든데, 아래 자전거를 만드는 예시를 보자.

public class Bike {
    
    private Wheel wheel;
    private Frame frame;
    
    Bike(Wheel wheel, Frame frame) { // Creator 패턴을 사용하지 않은 생성자 1
        this.wheel = wheel;
        this.frame = frame;
    }
    
    Bike(int weelWid, int frameLen) { // Creator 패턴을 사용한 생성자 2
        this.weel = new Wheel(weelWid);
        this.frame = new Frame(frameLen);
    }
    
    public static void main(String[] args) {
        // 생성자 1을 사용해 객체 생성
        Wheel wheel = new Wheel(24);
        Frame frame = new Frame(52);
        Bike bike1 = new Bike(wheel, frame);
        
        // 생성자 2를 사용해 객체 생성
        Bike bike2 = new Bike(24, 52);
    }
}

생성자 2에선 Creator 패턴에 따라 Wheel, Frame 객체 생성을 이 객체를 일부로 하는 Bike 객체에서 직접 생성했다.

생각 없이 코딩하다 보면 생성자 1 처럼 객체를 생성하게 되는데, 이 패턴을 고민하면서 객체를 생성해 보자.

 

3. Controller

 - 시스템 이벤트(사용자의 요청)를 처리할 객체를 만들자.

 

4. Low Coupling

 - 객체들간, 서브 시스템들 간의 상호 의존도가 낮게 책임을 부여

 - Low Coupling은 각 객체, 서브시스템의 재사용성을 높이고, 시스템 관리에 편하게 한다.

 - Object-Orinted 시스템은 각 객체들 간의 Comunication을 통해 비즈니스 완성

     - 따라서 각 객체들 상에 Coupling이 존재하지 않을 수는 없다.

 

5. High Cohesion

 - 각 객체가 밀접하게 연관된 책임들만 가지도록 구성하라.

 - 한 객체, 한 시스템이 자기 자신이 부여받은 책임만을 수행하도록 구성되있다면, 자연스럽게 Low Coupling이 된다.

 

6. Polymorphism : 다형성

 - 객체의 종류에 따라 행동양식이 바뀐다면, Polymorphism 기능을 사용하라.

 - 조건문을 통해 종류레 따라 행동을 바꾸지 말고 Polymorphism을 사용.

두 개의 다른 SilverSeller에서 SIlver의 가격을 가져오는 프로그램을 작성할 때,

If 문을 사용해 각 Seller에게서 가격을 받아오지 말고,

Seller를 하나의 abstract Class를 extends (혹은 Interface implements) 하면 if문 사용 없이 할 수 있다.

 

SilverSeller를 하나로 묶어줄 abstract class

public abstract class GetSilverPrice {
    abstract public double getPricdOfSilver();
    
    public String getName() {
        return null;
    }
}

판매처 1: ABCSilver

public class ABCSilver extends GetSilverPrice{
    
    private String silverPrice = "SLV 30.66";
    
    private String name = "ABC Silver";
    
    public double getPriceOfSilver() {
        String stringPrice = silverPrice.substring(4);
        
        return Double.paesDouble(StringPrice);
        
    }
    
    publuc String getName() { return name; }

}

판매처 2: XYZSilver

public class XYZSilver extends GetSIlverPrice{

    private String silverPrice = "30.67 Silver Ask price";
    private String name = "XYZ Silver";
    
    public double getPriceOfSilver() {
        
        Strung[] stringprice = silverPrice.split(" ");
        return Double.parseDouble(stringPrice[0]);
    
    }
    
    public String getName(){ return name; }
    
}

if문 없이 각 Seller를 조회하는 방법

public class SilverPrices {
    
    public static void main(String[] args{
        List<GetSilverPrice> silverSellers = new ArrayList<>();
        
        silverSellers.add(new ABCSilver());
        silverSellers.add(new XYZSilver());
        
        for(GetSilverPrice silverPrice : silverSellers){
            System.out.println(silverPrice.getName() + ": " + silverPrice.getPriceOfSilver());
        }
    }
}

 

7. Pure Fabrication

 - 도메인에 관련된 문제를 대표하는 것이 아니라면, 기능적인 책임을 별도로 한 곳으로 관리하는 객체를 만들자.

 

아래와 같이 Sale 클래스에 데이터 정보와 그 데이터에 접근하는 메서드들이 있다. (코드는 생략)

public class Sale {
    Data data;
    String[] items;
    double[] itemPrices;
    double pruchaseAmt;
    
    public void InsertInDatabase() {
        //...
    }
    
    public void UpdateDatabase() {
        //...
    }
    
    public void DeleteFromDatabase() {
        //...
    }
}

이 경우 아래와 같이 데이터에 접근하는 클래스를 분리하는 것이 Pure Fabrication Pattern이다.

public class Sale {
    Data data;
    String[] items;
    double[] itemPrices;
    double pruchaseAmt;
}


public class AccessDatabase {
    Sale sale;
    
    public void InsertInDatabase() {
        //...
    }
    
    public void UpdateDatabase() {
        //...
    }
    
    public void DeleteFromDatabase() {
        //...
    }
}

8. Indirection

 - 두 객체 사이의 직접적인 Coupling을 피하고 싶다면, 그 사이에 다른 매개체를 통해 전달받아라.

   - 주로 인터페이스를 많이 쓰인다.

- GOF의 Adapter, Bridge, Facade, Observer, Mediator 가 해당된다.

public interface EnemyAttacker{

    public void attack();
    
}
public class EnemyTank implements EnemyAttacker{

    public void attack() {
        System.out.println("Tank fires 2 missiles");
    }
}
public class EnemyRobot {
    public void jumpOnEnemy(){
        System.out.println("Robot jumps on the enemy");
    }
}
public class EnemyRoboyAdapter implement EnemyAttacker {

    private EnemyRobot robot;
    
    public EnemyRobotAdapter(EnemyRobot robot) {
        this.robot = roboy;
    }

    public void attack() {
        robot.jumpOnEnemy();
    }
    
}
public class TestAdapter{
    public static void main(String[] args) {
        EmentAttacker tank = new EnemyTank();
        
        EnemyRobot robot = new EnemyRobot();
        
        EnemyAttacker robotAdapter = new EnemyRobotAdapter(robot);
        
        tank.attack();
        robotAdapter.attack();
    }
}

 

9. Protected Variations

 - 변경될 여지가 있는 곳에 안정된 인터페이스를 정의해 사용하자.

 

 

 

출처: https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8586826397

'코딩 > Software Architecture' 카테고리의 다른 글

[GoF Design Pattern] State  (1) 2022.12.24
[GoF Design Pattern] Abstract Factory  (0) 2022.12.24
[GoF Design Pattern] Chain of Responsibility  (0) 2022.12.06
[GoF Design Pattern] Mediator  (0) 2022.12.06
[GoF Design Pattern] Decorator  (0) 2022.12.05