CodeGym /Java 博客 /随机的 /策略设计模式
John Squirrels
第 41 级
San Francisco

策略设计模式

已在 随机的 群组中发布
你好!在今天的课程中,我们将讨论策略模式。在前面的课程中,我们已经简要地熟悉了继承的概念。如果您忘记了,我会提醒您,该术语指的是针对常见编程任务的标准解决方案。在 CodeGym,我们经常说您几乎可以通过谷歌搜索任何问题的答案。这是因为你的任务,无论是什么,可能已经被其他人成功解决了。模式是最常见任务的可靠解决方案,或者是解决问题情况的方法。这些就像您不需要自己重新发明的“轮子”,但您确实需要知道如何以及何时使用它们:) 模式的另一个目的是促进统一架构。阅读别人的代码绝非易事!每个人写的代码不一样,因为同一个任务可以通过多种方式解决。但是模式的使用可以帮助不同的程序员理解编程逻辑,而无需深入研究每一行代码(即使是第一次看到它!)今天我们来看一种最常见的设计模式,称为“策略”。 设计模式:策略 - 2想象一下,我们正在编写一个程序,该程序将主动使用 Conveyance 对象。我们的程序到底做什么并不重要。我们创建了一个包含一个Conveyance父类和三个子类的类层次结构:SedanTruckF1Car

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {
}

public class Truck extends Conveyance {
}

public class F1Car extends Conveyance {
}
所有三个子类都从父类继承了两个标准方法:go()stop()。我们的程序很简单:我们的车只能向前行驶并踩刹车。继续我们的工作,我们决定给汽车一个新方法:fill()(意思是“加满油箱”)。我们将它添加到Conveyance父类中:

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
  
   public void fill() {
       System.out.println("Refueling!");
   }
}
这么简单的情况真的会出问题吗?事实上,他们已经... 设计模式:策略 - 3

public class Stroller extends Conveyance {

   public void fill() {
      
       // Hmm... This is a stroller for children. It doesn't need to be refueled :/
   }
}
我们的程序现在有一个交通工具(婴儿车),不太符合一般概念。它可以有踏板或无线电控制,但有一件事是肯定的——它没有任何地方可以加油。我们的类层次结构导致常用方法被不需要它们的类继承。遇到这种情况怎么办?好吧,我们可以覆盖Stroller类中的fill()方法,这样当您尝试给婴儿车加油时什么也不会发生:

public class Stroller extends Conveyance {

   @Override
   public void fill() {
       System.out.println("A stroller cannot be refueled!");
   }
}
但是,如果除了重复代码之外没有其他原因,这很难称为成功的解决方案。例如,大多数类会使用父类的方法,但其余类将被迫重写它。如果我们有 15 个类并且我们必须重写其中 5-6 个的行为,代码重复将变得相当广泛。也许接口可以帮助我们?例如,像这样:

public interface Fillable {
  
   public void fill();
}
我们将使用一个fill()方法创建一个Fillable接口。那么,那些需要加油的交通工具就会实现这个接口,而其他交通工具(比如我们的婴儿车)则不会。但是这个选项不适合我们。将来,我们的类层次结构可能会变得非常大(想象一下世界上有多少种不同类型的交通工具)。我们放弃了之前涉及继承的版本,因为我们不想覆盖fill()方法很多很多次。现在我们必须在每个班级实施它!如果我们有 50 个呢?而且,如果我们的程序会频繁更改(对于实际程序几乎总是如此!),我们将不得不匆忙完成所有 50 个类并手动更改每个类的行为。那么,在这种情况下,我们到底应该怎么做呢?为了解决我们的问题,我们将选择不同的方式。也就是说,我们会将类的行为与类本身分开。这意味着什么?如您所知,每个对象都有状态(一组数据)和行为(一组方法)。我们的运输类的行为包括三个方法:go()stop()fill()。前两种方法就可以了。但是我们将把第三种方法移出传送类。这会将行为与类分离(更准确地说,它只会分离部分行为,因为前两个方法将保留在原处)。那么我们应该把我们的fill()方法放在哪里呢?什么都没有想到:/它似乎正是它应该在的地方。我们将把它移到一个单独的界面:FillStrategy

public interface FillStrategy {

   public void fill();
}
为什么我们需要这样的接口?一切都很简单。现在我们可以创建几个实现这个接口的类:

public class HybridFillStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas or electricity — your choice!");
   }
}

public class F1PitstopStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas only after all other pit stop procedures are complete!");
   }
}

public class StandardFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Just refuel with gas!");
   }
}
我们创建了三种行为策略:一种用于普通汽车,一种用于混合动力车,一种用于一级方程式赛车。每种策略都实现不同的加油算法。在我们的例子中,我们只是在控制台上显示一个字符串,但每个方法都可能包含一些复杂的逻辑。我们接下来做什么?

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
  
}
我们将FillStrategy接口用作Conveyance父类中的一个字段。请注意,我们并没有指出具体的实现——我们使用的是接口。汽车类将需要FillStrategy接口的特定实现:

public class F1Car extends Conveyance {

   public F1Car() {
       this.fillStrategy = new F1PitstopStrategy();
   }
}

public class HybridCar extends Conveyance {

   public HybridCar() {
       this.fillStrategy = new HybridFillStrategy();
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       this.fillStrategy = new StandardFillStrategy();
   }
}

让我们看看我们得到了什么!

public class Main {

   public static void main(String[] args) {

       Conveyance sedan = new Sedan();
       Conveyance hybrid = new HybridCar();
       Conveyance f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
控制台输出:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
伟大的!加油过程正常进行!顺便说一句,没有什么能阻止我们在构造函数中使用策略作为参数!例如,像这样:

public class Conveyance {

   private FillStrategy fillStrategy;

   public Conveyance(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }

   public void fill() {
       this.fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       super(new StandardFillStrategy());
   }
}



public class HybridCar extends Conveyance {

   public HybridCar() {
       super(new HybridFillStrategy());
   }
}

public class F1Car extends Conveyance {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
让我们运行我们的main()方法(保持不变)。我们得到相同的结果!控制台输出:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
策略设计模式定义了一系列算法,封装了它们中的每一个,并确保它们可以互换。它允许您修改算法,而不管客户端如何使用它们(这个定义取自“Head First Design Patterns”一书,对我来说似乎很棒)。 设计模式:策略 - 4我们已经在具有不同实现的单独接口中指定了我们感兴趣的算法系列(给汽车加油的方法)。我们将它们与汽车本身分开。现在,如果我们需要对特定的加油算法进行任何更改,它不会以任何方式影响我们的汽车类别。为了实现互换性,我们只需要向我们的Conveyance类添加一个 setter 方法:

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
现在我们可以即时更改策略:

public class Main {

   public static void main(String[] args) {

       Stroller stroller= new Stroller();
       stroller.setFillStrategy(new StandardFillStrategy());

       stroller.fill();
   }
}
如果婴儿车突然开始使用汽油运行,我们的程序将准备好处理这种情况:) 仅此而已!您又学到了一种设计模式,这在处理实际项目时无疑是必不可少的并且很有帮助 :) 下次见!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION