Design Patterns - Strategy Patterns

        Recently, I was learning design patterns. I read the design pattern of HeadFirst. It was very vivid. It explained several commonly used design patterns in detail. It has a lot of benefits for beginners. This series of articles summarizes my learning results. Based on the code implementation, there is also my personal understanding.

        This blog, talk about the strategy pattern.

        First briefly, the idea of ​​​​this article is based on a duck class. Different duck subclasses have different behaviors and the same behavior, and can add behaviors to all duck subclasses uniformly, or to a certain subclass. A class-specific addition or modification of a behavior.

        For those who are inexperienced in development, first we will think of defining a parent class, and then subclasses inherit or override the public methods of the parent class and add their own specific methods. code show as below:

public abstract class Duck {
     //All ducks can call and swim, so the superclass directly implements this part of the code
 public void quack(){    
        System.out.println("I can quack guaguagua");
    }
    public void swim(){
        System.out.println("I can swim");
    }
    //Different ducks have different shapes, so it is an abstract method, and the subclass implements
 public abstract void display();    
}
public class GreenheadDuck extends Duck {
    @Override
public void display() {    
        System.out.println("I have green head");
    }
}
public class RedheadDuck extends Duck {
    @Override
public void display() {    
        System.out.println("I have red head");
    }
}

    It's easy to do without stress.

    But now the requirements have changed, and the ducks are required to be able to fly. Many people would think of adding a fly() method to the superclass. But what if not all ducks could fly. For example, a Rubber Duck, other ducks can fly, but it cannot fly. Oh, yes, you can rewrite the fly() of the parent class in the RubberDuck class without any implementation code in it. is a method.

But what if the requirement is to add a wooden duck and it can't fly or bark? Override fly() and quack()?

    This is obviously not a good solution.

    tip1: How about using the interface? There is a flyable interface and a quackable interface, which can be called and fly to implement the corresponding interfaces respectively. This achieves our intended functionality. When you actually implement this idea, you find that when you have 40 subclasses, thirty of them fly the same way, but you have to rewrite the fly method thirty times. This practice is ridiculous.

    When designing programs, there is a design principle that allows you to find ideas:

Identify areas of your application that might change, isolate them, and don't mix them with code that doesn't change.

Among the current requirements, the parts that may change are the way the ducks croak and the way they fly. In order to separate these two behaviors from the class, we create a new set of classes to represent each new behavior. How to design it? Another design principle will give us ideas:

Program for the interface, not for the implementation.

We will design multiple interfaces to implement various behaviors, and then for specific behaviors, different implementations correspond to the methods in the interface. The difference from before is that this time it is a specific behavior class. In the duck subclass, we will instantiate the specific behavior class through a variable and call the method in the behavior class to implement different behaviors of different duck subclasses. Like this (demo flight only):

public interface FlyBehavior {
    void fly();
}
public class FlyWithWings implements FlyBehavior {
    @Override
public void fly() {    
        System.out.println("I can fly");
    }
}
public class FlyWithNothing implements FlyBehavior {
    @Override
public void fly() {    
        System.out.println("I can't fly");
    }
}

The next question is how to call the different behavior methods in the duck class. Because our idea is that all subclasses call fly, but there is nothing in the duck implementation code that can't fly. So we can add the following code to the superclass:

FlyBehavior flyBehavior ;

public void performQuack(){
    flyBehavior.fly();
}
We replace the fly() method with the performQuack() method; this way the process of invoking the method is delegated to the behavior class,
And you can call the performQuack() method independently at runtime.
public interface FlyBehavior {
    void fly();
}

We need to add the following code in the subclass implementation to determine the specific behavior class

    //The red duck can fly, so instantiate the FlyWithWings class 

public RedheadDuck(){
    flyBehavior=new FlyWithWings();
}
//The rubber duck can't fly, so instantiate the FlyWithNothing class
public RubberDuck(){
    flyBehavior=new FlyWithNothing();
}
 
 

Test code:

public static void main(String[] args) {
   Duck redduck=new RedheadDuck();
redduck.performQuack();
redduck.swim();
redduck.display();

Duck rubberDuck=new RubberDuck();
rubberDuck.performQuack();
rubberDuck.swim();
rubberDuck.display();
}
result:
I can fly
I can swim
I have red head
I can't fly
I can swim
I have Rubber head

Some experienced people will find that our usual class does not want all of its methods to be hard-coded, especially in the part we extract that may change at any time, we prefer to decide the methods in the class by ourselves at runtime . So it is not advisable for us to instantiate the behavior class directly in the subclass.

We add the get set method of the behavior interface in the superclass

public FlyBehavior getFlyBehavior() {
    return flyBehavior;
}

public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
}
这样我们在运行时如果想要改变某个子类的行为,可以直接设定
Duck redduck=new RedheadDuck();
redduck.performQuack();
//动态改变红头鸭子行为,使其不能飞行
redduck.setFlyBehavior(new FlyWithNothing());
redduck.performQuack();
redduck.swim();
redduck.display();
结果:

I can fly

I can't fly

I can swim

I have red head


经过上面一系列历程,我们解决的面对的一些问题,可以自由的增添子类,动态的改变特定方法。让我们回顾一下我们这次程序设计的一些亮点。

1.我们把可能会改变的每种鸭子的行为用一个行为类进行了封装,不会改变的方法在超类中进行了实现。当一种鸭子的行为要发生改变的时候,我们可以在运行时动态的用另一个行为类进行替换;——这也是策略模式的中心思想。

2.我们针对接口编程

    所谓针对接口,并不是真正意义上的interface,可以使一个抽象类。重点使用多态就对了。

本例中,我们Duck redduck=new RedheadDuck();   redduck.performQuack();

这样你不用管performQuack里面是什么东西,你只需要知道,是一个鸭子,就要调用这个方法就够了。

之前我们会 RedheadDuck redheadDuck=new RedheadDuck(); redheadDuck.fly();

甚至我们不需要在类中硬编码实例化新的子类,可以 duck=getDuck(); duck.performQuack();这样在运行时具体制定要实现的对象。

3.我们使用组合,而非继承。

    所谓组合就是将两个或者更多的类在一个类中使用,在当前类中调用其他类的方法,而不是继承来所有的方法。这样可以使程序更具有弹性,随时调用你想用的任意方法。而“tip1”中的思想相当于每个类都实现一个接口,重写其中方法,这使得程序变得难以修改,而我们在超类中

FlyBehavior flyBehavior;

public void performQuack(){

    flyBehavior.fly();
}
 
 

然后在子类中确定FlyBehavior的具体实现类,相当于调用了另一个类的fly()方法。这样我们不仅可以将fly()中的代码封装起来,更可以在运行时动态的改变行为。

再看看策略模式的定义:定义一个算法族(指一系列的算法),分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

本例中的代码都很简单,但设计模式是思想,学习的路还很远~

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325566917&siteId=291194637