jeudi 16 avril 2020

Decorator + State patterns: what if a method I'm decorating depends on the State of the object?

The PC class has several methods - turnON(), turnOFF() and doWork(). The doWork() method can be decorated, adding additional functionality to the PC.

Every method changes its behaviour based on the state of the PC - so when you try to turnON() the PC, the method just delegated the execution of the behavior to the current state of the PC, and it handles it properly.

However, if I want to have doWork(), the method which can be decorated, also depend on the state (PC cannot doWork() if the state is PCStateOFF or PCStateIncomplete), what should I do?

Since the State pattern states that the execution of the behavior has to be delegated to the state, I need to make PCStateON, PCStateOFF and PCStateIncomplete also handle doWork(). But if its PC that is decorated, then I am not checking whether the PC is ON before executing the added functionality. I could inject some if/else statements inside each doWork() implementation of the PCDecorators, to only add functionality if the current state is the one that allows it, but then the whole idea of State is to do away with if/else.

Then I could actually decorate the PCStateON class, since this is the only one that changes when PC is decorated...

How do I execute this properly?

Common interface for PC and PCDecorator:

interface IPC {
    public void doWork();
}

The PC class:

public class PC implements IPC {

    PCState state;

    public PC(){
        this.state = new PCStateIncomplete(this);
    }

    public void turnOn(){
        this.state.turnOn();
    }

    public void turnOff(){
        this.state.turnOff();
    }

    public void changeState(PCState state){
        this.state = state;
    }

    public void doWork() {
        System.out.print("\nWorking... ");
    }
}

Common Decorator class and Decorators:

abstract public class PCDecorator implements IPC{
    protected IPC pc;
    abstract public void doWork();
}
public class PCGamingDecorator extends PCDecorator {
    public PCGamingDecorator(IPC computer) {
        this.pc = computer;
    }

    @Override
    public void doWork(){
        pc.doWork();
        System.out.print("Gaming... ");
    }
}
public class PCMiningDecorator extends PCDecorator{
    public PCMiningDecorator(IPC computer) {
        this.pc = computer;
    }

    @Override
    public void doWork(){
        pc.doWork();
        System.out.print("Mining... ");
    }
}
public class PCStreamingDecorator extends PCDecorator {
    public PCStreamingDecorator(IPC computer) {
        this.pc = computer;
    }

    @Override
    public void doWork(){
        pc.doWork();
        System.out.print("Streaming... ");
    }
}

Common state and states:

public interface PCState {
    public void turnOn();
    public void turnOff();
}
public class PCStateIncomplete implements PCState {
    PC pc;
    public PCStateIncomplete(PC pc){
        this.pc = pc;
    }
    public void turnOn(){
        System.out.println("Cannot do Work - PC is Incomplete!");
    }
    public void turnOff(){
        System.out.println("Cannot turn OFF - PC is Incomplete!");
    }
}
public class PCStateOFF implements PCState {
    PC pc;
    public PCStateOFF(PC pc){
        this.pc = pc;
    }
    public void turnOn(){
        this.pc.changeState(new PCStateON(this.pc));
        System.out.println("PC has been turned ON!");
    }
    public void turnOff(){
        System.out.println("PC is already OFF!");
    }
}
public class PCStateON implements PCState {
    PC pc;
    public PCStateON(PC pc){
        this.pc = pc;
    }
    public void turnOn(){
        System.out.println("PC is already ON!");
    }
    public void turnOff(){
        this.pc.changeState(new PCStateOFF(this.pc));
        System.out.println("PC has been turned OFF!");
    }
}

Aucun commentaire:

Enregistrer un commentaire