CS/λ””μžμΈ νŒ¨ν„΄

[λ””μžμΈ νŒ¨ν„΄] μƒνƒœ(State) νŒ¨ν„΄

벼리01 2024. 1. 17. 20:57

μƒνƒœ νŒ¨ν„΄μ΄λž€ ν–‰μœ„ νŒ¨ν„΄ 쀑 ν•˜λ‚˜λ‘œ, 객체가 νŠΉμ • μƒνƒœμ— 따라 λ™μž‘μ„ λ‹€λ₯΄κ²Œ ν•˜λŠ” μƒν™©μ—μ„œ 쑰건문으둜 λΆ„κΈ°ν•˜λŠ” λŒ€μ‹  μƒνƒœ 자체λ₯Ό κ°μ²΄ν™”ν•˜μ—¬ 행동 결정을 μœ„μž„ν•˜λŠ” νŒ¨ν„΄μ„ λ§ν•œλ‹€. μƒνƒœλž€ 객체가 κ°€μ§ˆ 수 μžˆλŠ” μ–΄λ–€ μ‘°κ±΄μ΄λ‚˜ 상황을 λ§ν•œλ‹€. 주둜 λ‹€μŒκ³Ό 같은 κ²½μš°μ— μ‚¬μš©ν•œλ‹€. 

 

1. 객체의 행동이 μƒνƒœμ— 따라 λ‹¬λΌμ§ˆ λ•Œ

2. 객체의 μ½”λ“œκ°€ μƒνƒœ μ „ν™˜μ— λ”°λ₯Έ 쑰건문으둜 λ³΅μž‘ν•΄μ§ˆ λ•Œ

 

μƒνƒœ ν΄λž˜μŠ€λŠ” μƒνƒœμ— 따라 행동을 κ΅¬ν˜„ν•˜κ³ , κ°μ²΄λŠ” κ·Έ μƒνƒœλ₯Ό κ°€μ§€λ©° μƒνƒœμ— 따라 μ μ ˆν•œ 행동을 μ·¨ν•œλ‹€. 예λ₯Ό λ“€μ–΄, μžνŒκΈ°λŠ” 돈이 μΆ©λΆ„νžˆ λ“€μ–΄μ˜€μ§€ μ•ŠμœΌλ©΄ λ²„νŠΌμ΄ ν™œμ„±ν™”λ˜μ§€ μ•ŠλŠ”λ‹€. μžνŒκΈ°λŠ” 돈이 λ“€μ–΄μ˜¨ μƒνƒœμ™€, 돈이 λ“€μ–΄μ˜€μ§€ μ•Šμ€ μƒνƒœμ— 따라 ν–‰μœ„κ°€ 달라진닀. 

 

 

// μƒνƒœ μΈν„°νŽ˜μ΄μŠ€
interface VendingMachineState {
    void insertCoin();
    void ejectCoin();
    void dispense();
}

 

// μƒνƒœ κ΅¬ν˜„ 클래슀
// 돈이 λ“€μ–΄μ˜€μ§€ μ•Šμ€ μƒνƒœ
class NoCoinState implements VendingMachineState {
    @Override
    public void insertCoin() {
        System.out.println("μž…κΈˆλ˜μ—ˆμŠ΅λ‹ˆλ‹€. HasCoinState μƒνƒœλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€.");
        // HasCoinState μƒνƒœλ‘œ μ „ν™˜ν•¨
        vendingMachine.setState(new HasCoinState());
    }

    @Override
    public void ejectCoin() {
        System.out.println("μž”λˆ μ—†μŒ.");
    }

    @Override
    public void dispense() {
        System.out.println("μž…κΈˆλ˜μ§€ μ•Šμ€ μƒνƒœ: λ¨Όμ € μž…κΈˆν•΄μ£Όμ„Έμš”.");
    }
}

class HasCoinState implements VendingMachineState {
    // 돈이 λ“€μ–΄μ˜¨ μƒνƒœμ˜ 행동을 μ •μ˜ν•œλ‹€.
}

 

// μƒνƒœλ₯Ό κ°€μ§„ 객체
class VendingMachine {
	// μƒνƒœλ₯Ό 멀버 ν•„λ“œλ‘œ κ°€μ§€κ³  있음.
    private VendingMachineState currentState;

    public VendingMachine() {
        // 초기 μƒνƒœ μ„€μ •
        currentState = new NoCoinState();
    }

    // μƒνƒœ λ³€κ²½
    public void setState(VendingMachineState state) {
        this.currentState = state;
    }

    // λ™μž‘μ„ ν˜„μž¬ μƒνƒœμ— μœ„μž„
    public void insertCoin() {
        currentState.insertCoin();
    }

    public void ejectCoin() {
        currentState.ejectCoin();
    }

    public void dispense() {
        currentState.dispense();
    }
}

 

 

public class Main {
    public static void main(String[] args) {
        VendingMachine vendingMachine = new VendingMachine();

        vendingMachine.insertCoin(); // μž…κΈˆλ˜μ—ˆμŠ΅λ‹ˆλ‹€. HasCoinState μƒνƒœλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€.
        vendingMachine.ejectCoin();  // μž”λˆμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.
        
        vendingMachine.dispense();   // μž…κΈˆλ˜μ§€ μ•Šμ€ μƒνƒœ: λ¨Όμ € μž…κΈˆν•΄μ£Όμ„Έμš”.
        vendingMachine.insertCoin(); // μž…κΈˆλ˜μ—ˆμŠ΅λ‹ˆλ‹€. HasCoinState μƒνƒœλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€.
        vendingMachine.dispense();   // μŒλ£Œκ°€ λ‚˜μ˜΅λ‹ˆλ‹€. NoCoinState μƒνƒœλ‘œ μ „ν™˜ν•©λ‹ˆλ‹€.
    }
}

 

 

μžνŒκΈ°λŠ” λˆμ„ κ°€μ§„ μƒνƒœ(`HasCoinState`)와 돈이 μ—†λŠ” μƒνƒœ(`NoCoinState`) 두가지 μƒνƒœλ₯Ό κ°€μ§ˆ 수 μžˆλ‹€. 같은 ν–‰μœ„λ₯Ό κ΅¬ν˜„ν•˜λ˜, μƒνƒœμ— 따라 λ‹€λ₯Έ 행동을 ν•œλ‹€. 상기 자판기 μ½”λ“œμ—μ„œλŠ” 두 μƒνƒœ λͺ¨λ‘ `insertCoin()` `ejectCoin()` `dispense()`λ₯Ό κ΅¬ν˜„ν•˜μ§€λ§Œ μƒνƒœμ— 따라 ν–‰μœ„κ°€ λ‹€λ₯΄κ²Œ μ˜€λ²„λΌμ΄λ”© λ˜μ–΄μžˆλ‹€. `NoCoinState`  μƒνƒœμ—μ„œλŠ” `dispense()` λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ μž…κΈˆλ˜μ§€ μ•Šμ€ μƒνƒœμ΄λ―€λ‘œ μŒλ£Œκ°€ λ‚˜μ˜€μ§€ μ•ŠλŠ”λ‹€. λ°˜λ©΄μ— `HasCoinState` μƒνƒœλΌλ©΄ 같은 `dispense()` λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄λ„ μŒλ£Œκ°€ λ‚˜μ˜¨λ‹€. λ§Œμ•½ μƒνƒœ 없이 λͺ¨λ“  상황을 κ΅¬ν˜„ν•˜λ € ν–ˆλ‹€λ©΄ μ½”λ“œκ°€ κΈΈκ³  λ³΅μž‘ν•΄μ‘Œμ„ 것이닀.

 

 

if(money - selectBaverage < 0){
	System.out.println("μž”μ•‘μ΄ λΆ€μ‘±ν•©λ‹ˆλ‹€.");
}else{
	System.out.println("μŒλ£Œκ°€ λ‚˜μ˜΅λ‹ˆλ‹€.");
}

if(currentMoney > 0){
	System.out.println("κ±°μŠ€λ¦„λˆμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.");
}else{
	System.our.println("κ±°μŠ€λ¦„λˆμ΄ μ—†μŠ΅λ‹ˆλ‹€.");
}

.
.
.

 

 

μ‹€μ œλ‘œ μƒνƒœκ°€ λ§Žμ•„μ§„λ‹€λ©΄ 쑰건문은 더 λ³΅μž‘ν•΄μ§€κ³  μ½”λ“œλŠ” κ±·μž‘μ„ 수 없이 κΈΈμ–΄μ§€κ²Œ λœλ‹€. 이처럼 가독성이 λ–¨μ–΄μ§€λŠ” 쑰건문 λŒ€μ‹  μƒνƒœ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ μœ μ§€λ³΄μˆ˜κ°€ μš©μ΄ν•΄μ§„λ‹€.

 

 

μ •μˆ˜λͺ¨λ“œ, 온수λͺ¨λ“œ, λƒ‰μˆ˜λͺ¨λ“œ, 어린이 보호 λͺ¨λ“œ, μ²­μ†Œ λͺ¨λ“œ, μ–ΌμŒ 유무의 μ—¬λŸ¬ μƒνƒœλ₯Ό κ°€μ§€κ³  μžˆλŠ” μ •μˆ˜κΈ°λ₯Ό μ½”λ“œλ‘œ μž‘μ„±ν•œλ‹€λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

 

//	μƒνƒœ μΈν„°νŽ˜μ΄μŠ€
interface WaterMode {
    void dispenseWater();
    void dispenseIce();

}

//	μƒνƒœ κ΅¬ν˜„
class HotWaterMode implements WaterMode{
    @Override
    public void dispenseWater() {
        System.out.println("μ˜¨μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public void dispenseIce() {
        System.out.println("μ–ΌμŒμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public String toString() {
        return "온수 λͺ¨λ“œ";
    }

}

class ColdWaterMode implements WaterMode {
    @Override
    public void dispenseWater() {
        System.out.println("λƒ‰μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public void dispenseIce() {
        System.out.println("μ–ΌμŒμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public String toString() {
        return "λƒ‰μˆ˜ λͺ¨λ“œ";
    }
}

class PurifiedWaterMode implements WaterMode {

    @Override
    public void dispenseWater() {
        System.out.println("μ •μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public void dispenseIce() {
        System.out.println("μ–ΌμŒμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.");
    }

    @Override
    public String toString() {
        return "μ •μˆ˜ λͺ¨λ“œ";
    }
}
class CleanMode implements WaterMode {
    @Override
    public void dispenseWater() {
        System.out.println("μ²­μ†Œκ°€ μ§„ν–‰ μ€‘μ΄λ―€λ‘œ 물이 λ‚˜μ˜€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
    }

    @Override
    public void dispenseIce() {
        System.out.println("μ²­μ†Œκ°€ μ§„ν–‰ μ€‘μ΄λ―€λ‘œ μ–ΌμŒμ΄ λ‚˜μ˜€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.");
    }

    @Override
    public String toString() {
        return "μ²­μ†Œ λͺ¨λ“œ";
    }
}

 

 

class WaterDispenser implements WaterMode{
    public WaterMode waterMode;
    public boolean childProtectMode;

    public WaterDispenser(WaterMode waterMode, boolean childProtect) {
        this.waterMode = waterMode;
        this.childProtectMode = childProtect;
    }


    @Override
    public void dispenseWater() {
        waterMode.dispenseWater();
    }

    @Override
    public void dispenseIce() {
        waterMode.dispenseIce();
    }

    public void setWaterMode(WaterMode newMode){
        if(childProtectMode && newMode instanceof HotWaterMode) {
            System.out.println("어린이 λͺ¨λ“œ ν™œμ„±ν™” : 온수 λͺ¨λ“œλ‘œ μ „ν™˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€.");
        }else{
            this.waterMode = newMode;
            System.out.println(newMode + "둜 μ „ν™˜ν•©λ‹ˆλ‹€.");
        }
    }

    public void setChildProtectMode(Boolean childProtectMode){
        System.out.println("어린이 λͺ¨λ“œλ₯Ό" + (childProtectMode ? " " : " λΉ„") + "ν™œμ„±ν™”ν•©λ‹ˆλ‹€.");
        this.childProtectMode = childProtectMode;
    }
}

 

 

//	ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ
public class main {

    public static void main(String[] args) {
        WaterDispenser waterDispenser = new WaterDispenser(new ColdWaterMode(), true);

        waterDispenser.dispenseWater();     //  λƒ‰μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.
        waterDispenser.dispenseIce();       //  μ–ΌμŒμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.

        //  μ •μˆ˜ λͺ¨λ“œλ‘œ μ „ν™˜
        waterDispenser.setWaterMode(new PurifiedWaterMode());

        waterDispenser.dispenseWater();     //  μ •μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.
        waterDispenser.setChildProtectMode(true);   //  어린이 λͺ¨λ“œλ₯Ό ν™œμ„±ν™”ν•©λ‹ˆλ‹€.

        waterDispenser.setWaterMode(new HotWaterMode());    //  어린이 λͺ¨λ“œ: 온수 λͺ¨λ“œλ‘œ μ „ν™˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

        waterDispenser.dispenseIce();   //  μ–ΌμŒμ΄ λ‚˜μ˜΅λ‹ˆλ‹€.

        //  μ²­μ†Œ λͺ¨λ“œλ‘œ μ „ν™˜
        waterDispenser.setWaterMode(new CleanMode());

        waterDispenser.dispenseWater();     //  μ²­μ†Œκ°€ μ§„ν–‰ μ€‘μ΄λ―€λ‘œ 물이 λ‚˜μ˜€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
        waterDispenser.dispenseIce();       //  μ²­μ†Œκ°€ μ§„ν–‰ μ€‘μ΄λ―€λ‘œ μ–ΌμŒμ΄ λ‚˜μ˜€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

        waterDispenser.setChildProtectMode(false);

        //  온수 λͺ¨λ“œλ‘œ μ „ν™˜
        waterDispenser.setWaterMode(new HotWaterMode());

        waterDispenser.dispenseWater();     //  μ˜¨μˆ˜κ°€ λ‚˜μ˜΅λ‹ˆλ‹€.

    }
}

 

 

μ •μˆ˜κΈ° 객체가 μƒνƒœμ— 따라 ν–‰μœ„κ°€ 달라지고 μžˆμœΌλ―€λ‘œ 마치 λ‹€λ₯Έ 객체둜 λ³€κ²½λ˜λŠ” κ²ƒμ²˜λŸΌ 보인닀. 이처럼 μƒνƒœ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ 객체 νŒ¨ν„΄μ΄ μƒνƒœμ— 따라 ν•˜λ‚˜μ˜ 객체둜 각기 λ‹€λ₯Έ λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆλ‹€. 

 

πŸ“Œ μ°Έκ³ 

상기 μ½”λ“œμ—μ„œ μƒνƒœλ₯Ό λ³€κ²½ν•  λ•Œλ§ˆλ‹€ 맀번 new μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό μƒμ„±ν•˜μ˜€μœΌλ‚˜ μ‹€μ œ μ‹€ν–‰ 쀑에 객체 생성을 λ‚¨λ°œν•˜λŠ” 것은 κ°€λΉ„μ§€ μ»¬λ ‰ν„°μ˜ 객체 제거 κ³Όμ •μ—μ„œ Stop-the-worldλ₯Ό μœ λ°œμ‹œν‚¬ 수 μžˆλ‹€. λ”°λΌμ„œ μƒνƒœ 객체λ₯Ό 싱글톀 νŒ¨ν„΄μœΌλ‘œ κ΅¬ν˜„ν•  것을 κ³ λ €ν•œλ‹€.

 

 

✨ μž₯점

1. ν•˜λ‚˜μ˜ 객체λ₯Ό ν•˜λ‚˜μ˜ ν•„λ“œλ§Œ μ‚¬μš©ν•˜μ—¬ μƒνƒœλ₯Ό λ³€κ²½ν•˜λ―€λ‘œ 효율적으둜 관리할 수 μžˆλ‹€. 

2. μ—¬λŸ¬ λΆ„κΈ°λ‘œ μž‘μ„±λœ 쑰건문에 λΉ„ν•΄ 가독성이 μš°μˆ˜ν•΄μ§„λ‹€.

3. μƒˆλ‘œμš΄ μƒνƒœλ₯Ό μΆ”κ°€ν•˜κ±°λ‚˜ λ³€κ²½ν•˜λ”λΌλ„ κΈ°μ‘΄ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€. μ΄λŠ” 객체 μ§€ν–₯ 5λŒ€ 원칙 쀑 ν•˜λ‚˜μΈ 개방 폐쇄 원칙(OCP: Open-Closed Principle)에 λΆ€ν•©ν•œλ‹€.

4. 객체가 λ‹€μŒμ— μ–΄λ–€ μƒνƒœλ‘œ 전이될지 λͺ…ν™•ν•˜κ²Œ νŒŒμ•…ν•  수 μžˆλ‹€.

5. 각 μƒνƒœκ°€ λ…λ¦½λœ 객체둜 μ‘΄μž¬ν•˜λ―€λ‘œ ν…ŒμŠ€νŠΈν•˜κΈ°μ— μš©μ΄ν•˜λ‹€.

 

 

πŸ‘Ž 단점

1. μƒνƒœκ°€ 증가할 λ•Œλ§ˆλ‹€ 클래슀 μˆ˜κ°€ μ¦κ°€ν•œλ‹€. μƒνƒœλ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ ν•΄λ‹Ή 클래슀 νŒŒμΌμ„ μ°Ύμ•„μ•Όν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ λ°œμƒν•  수 μžˆλ‹€.

2. μƒνƒœ κ°„μ˜ κ³΅ν†΅λœ λ™μž‘μ΄ 많고 크게 λ‹€λ₯΄μ§€ μ•Šμ€ 경우 였히렀 μœ μ§€ λ³΄μˆ˜μ— 어렀움을 더할 수 μžˆλ‹€.

3. λͺ¨λ“  μƒνƒœκ°€ 미리 μ •μ˜λ˜μ–΄ μžˆμœΌλ―€λ‘œ μœ μ—°ν•˜κ²Œ λ™μž‘μ„ λ³€κ²½ν•˜λŠ” 것이 μ–΄λ €μšΈ 수 μžˆλ‹€.

μƒνƒœ νŒ¨ν„΄μ„ μ‚¬μš©ν•¨μœΌλ‘œμ¨ μ·¨ν•˜λŠ” μž₯점이 단점보닀 적닀면 λ‹€λ₯Έ λ””μžμΈ νŒ¨ν„΄μ„ κ³ λ €ν•œλ‹€.