简体   繁体   中英

Is this a correct implementation of the strategy pattern (JAVA, Fly method of ducks)?

I just want a quick lookover that I implemented the different fly strategies correctly.

The program simply consists of a duck class that uses an interface for its fly method . The interface has different implementations (namely SimpleFly and NoFly ), and a switch statement chooses the correct method based on the specie enum.

As I understand, the strategy pattern is meant to avoid duplicate code between child classes at the same level, which decreases maintainability and extensibility. So instead we abstract out the related algorithms to interfaces and choose them as needed.

CODE:

package DesignPatterns;

//Here we will separate the fly method of ducks into implementations of an internface and then instantiate ducks with those attributes

interface IFly {
    
    public void fly();
    
}

//These are called strategies
class SimpleFly implements IFly {

    @Override
    public void fly() {
        System.out.println("Quack Quack i am flying in the air");
    }
    
}

class NoFly implements IFly {
    
    @Override
    public void fly() {
        System.out.println("I cannot fly.");
    }
    
}

//Now the base class just has to implement one of these strategies
class Duck {
    
    private IFly flyType;
    
    public enum SPECIE {
        WILD, CITY, RUBBER
    }
    
    public Duck(SPECIE specie) {
        switch(specie) {
            
            //Here just select the algorithms you want to assign to each type of DUCK. More flexible than horizontal code between species.
            case WILD:
            case CITY:
                this.flyType = new SimpleFly();
                break;
            case RUBBER:
                this.flyType = new NoFly();
                break;
            default:
                //If a new enum is defined but no definition yet, this stops code from breaking
                this.flyType = new SimpleFly();
        }
    }
    
    public void fly() {
        flyType.fly();
    }
    
}

The output is correct as in this example:

        Duck rubberDuck = new Duck(Duck.SPECIE.RUBBER);
        Duck normalDuck = new Duck(Duck.SPECIE.WILD);
        
        rubberDuck.fly();
        normalDuck.fly();

Yields:

I cannot fly.
Quack Quack i am flying in the air

Thank you in advance and please let me know about any gaps in my knowledge, Sshawarma

I would point out a couple issues of semantics and terminology.

  • It's confusing to have a method named fly that can be implemented as not flying. Naming the method tryToFly or documenting the method as merely an attempt are two ways of addressing this confusion. The software principle to reference here is Liskov Substitution.
  • The base class does not implement one of the strategies; rather, it composes a strategy. The purpose of the Strategy pattern is to avoid subclassing through composition.
  • To reiterate one of the comments, Duck should accept an instance of IFly directly in its constructor (or a setter method) rather than switching on an enum. Another goal of the Strategy pattern is to avoid branching logic.

The essence of the pattern is that you've avoided creating multiple subclasses of Duck by instead creating multiple implementations of IFly . This has the advantage that those IFly implementations can be reused without a complex inheritance hierarchy, eg WILD and CITY can share one strategy.

As mentioned in the comments, strategies also have the advantage that a Duck could change its strategy at runtime. For example, IFly might be implemented by Soar and Glide so that a Duck would switch between these different strategies depending on the wind.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM