你好!在前面的讲座中,我们已经接触过“设计模式”的概念。如果您忘记了,让我们提醒您:该术语表示编程中常见问题的某种标准解决方案。 在 JavaRush,我们经常说几乎任何问题的答案都可以通过 Google 搜索到。因此,有人可能已经成功解决了与您类似的问题。因此,模式是针对最常见问题的经过时间考验和实践检验的解决方案或解决问题情况的方法。这些正是“自行车”,您在任何情况下都不需要自己发明,但您需要知道如何以及何时应用它们:) 模式的另一个任务是将架构引入单一标准。阅读别人的代码并不是一件容易的事!每个人写的都不一样,因为同样的问题可以有很多种解决方式。但是模式的使用可以让不同的程序员无需深入研究每一行代码就可以理解程序的逻辑(即使他们是第一次看到它!)今天我们将看看一种最常见的模式,称为“策略”。 假设我们正在编写一个主动使用 Car 对象的程序。在这种情况下,我们的程序到底做什么并不特别重要。为此,我们创建了一个具有一个父类
Auto
和三个子类的继承系统:Sedan
、Truck
和F1Car
。
public class Auto {
public void gas() {
System.out.println("Едем вперед");
}
public void stop() {
System.out.println("Тормозим!");
}
}
public class Sedan extends Auto {
}
public class Truck extends Auto {
}
public class F1Car extends Auto {
}
所有三个子类都从父类继承了两个标准方法 -gas()
我们stop()
的程序非常简单:汽车只能向前行驶和制动。继续我们的工作,我们决定为汽车添加一种新方法 - fill()
(加油)。让我们将它添加到父类中Auto
:
public class Auto {
public void gas() {
System.out.println("Едем вперед");
}
public void stop() {
System.out.println("Тормозим!");
}
public void fill() {
System.out.println("Заправить бензин!");
}
}
这么简单的情况,似乎也会出现问题?嗯,事实上,他们已经出现了……
public class ChildrenBuggies extends Auto {
public void fill() {
//хм... Это детский багги, его не надо заправлять :/
}
}
我们的程序中出现了一辆不符合一般概念的汽车——儿童车。它可能是踏板驱动的或无线电控制的,但有一件事是肯定的——没有地方可以往里面加汽油。我们的继承方案导致我们放弃了通用方法,甚至给不需要它们的类。遇到这样的情况我们该怎么办呢?好吧,例如,您可以重写fill()
类中的方法ChildrenBuggies
,这样当您尝试给越野车加油时,就不会发生任何事情:
public class ChildrenBuggies extends Auto {
@Override
public void fill() {
System.out.println("Игрушечную машину нельзя заправить!");
}
}
但这个解决方案很难说是成功的,至少因为代码重复。例如,大多数类将使用父类中的方法,但其他类将被迫覆盖它。如果我们有 15 个类,并且在 5-6 个类中我们被迫重写行为,那么代码重复将变得相当广泛。也许接口可以帮助我们?例如,这个:
public interface Fillable {
public void fill();
}
我们将Fillable
使用一种方法创建一个接口fill()
。因此,那些需要加油的汽车将实现这个接口,但其他汽车(例如我们的越野车)不会。但这个选择也不适合我们。我们的类层次结构将来可能会增长到非常大的数量(想象一下世界上有多少种不同类型的汽车)。我们放弃了之前的继承选项,因为我们不想覆盖fill()
. 这里我们要在每堂课中贯彻落实!如果我们有 50 个怎么办?如果对我们的程序进行频繁的更改(在实际程序中这几乎总是会发生!),我们将不得不在所有 50 个类之间徘徊并手动更改每个类的行为。那么我们最终应该做什么呢?为了解决我们的问题,让我们选择一条不同的道路。也就是说,让我们将类的行为与类本身分开。这是什么意思?如您所知,任何对象都有状态(一组数据)和行为(一组方法)。我们的机器类的行为由三个方法组成 - gas()
、stop()
和fill()
。前两种方法都很好。但我们将把第三个方法移到类之外Auto
。这将是行为与类的分离(更准确地说,我们仅分离部分行为 - 前两个方法保持不变)。我们应该把我们的方法移到哪里fill()
?没有什么立即浮现在脑海中:/他似乎完全处于他的位置。我们将把它移动到一个单独的界面 - FillStrategy
!
public interface FillStrategy {
public void fill();
}
为什么我们需要这个接口?这很简单。现在我们可以创建几个实现该接口的类:
public class HybridFillStrategy implements FillStrategy {
@Override
public void fill() {
System.out.println("Заправляем бензином or электричеством на выбор!");
}
}
public class F1PitstopStrategy implements FillStrategy {
@Override
public void fill() {
System.out.println("Заправляем бензин только после всех остальных procedures пит-стопа!");
}
}
public class StandartFillStrategy implements FillStrategy {
@Override
public void fill() {
System.out.println("Просто заправляем бензин!");
}
}
我们为传统汽车、混合动力汽车和一级方程式汽车制定了三种行为策略。每个策略都实现单独的加油算法。在我们的例子中,这只是输出到控制台,但方法内部可能有一些复杂的逻辑。接下来我们应该做什么呢?
public class Auto {
FillStrategy fillStrategy;
public void fill() {
fillStrategy.fill();
}
public void gas() {
System.out.println("Едем вперед");
}
public void stop() {
System.out.println("Тормозим!");
}
}
我们使用我们的接口FillStrategy
作为父类中的字段Auto
。请注意:我们没有指定具体的实现,而是使用接口。我们需要FillStrategy
在子汽车类中具体实现该接口:
public class F1Car extends Auto {
public F1Car() {
this.fillStrategy = new F1PitstopStrategy();
}
}
public class HybridAuto extends Auto {
public HybridAuto() {
this.fillStrategy = new HybridFillStrategy();
}
}
public class Sedan extends Auto {
public Sedan() {
this.fillStrategy = new StandartFillStrategy();
}
}
让我们看看我们得到了什么:
public class Main {
public static void main(String[] args) {
Auto sedan = new Sedan();
Auto hybrid = new HybridAuto();
Auto f1car = new F1Car();
sedan.fill();
hybrid.fill();
f1car.fill();
}
}
控制台输出:
Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
太好了,加油过程正常进行!顺便说一句,没有什么可以阻止我们在构造函数中使用策略作为参数!例如,像这样:
public class Auto {
private FillStrategy fillStrategy;
public Auto(FillStrategy fillStrategy) {
this.fillStrategy = fillStrategy;
}
public void fill() {
this.fillStrategy.fill();
}
public void gas() {
System.out.println("Едем вперед");
}
public void stop() {
System.out.println("Тормозим!");
}
}
public class Sedan extends Auto {
public Sedan() {
super(new StandartFillStrategy());
}
}
public class HybridAuto extends Auto {
public HybridAuto() {
super(new HybridFillStrategy());
}
}
public class F1Car extends Auto {
public F1Car() {
super(new F1PitstopStrategy());
}
}
让我们运行我们的方法main()
(它保持不变)并得到相同的结果!控制台输出:
Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
策略模式定义了一系列算法,封装了它们中的每一个,并确保它们是可互换的。它允许您修改算法,无论它们在客户端的使用如何(这个定义取自《探索设计模式》一书,在我看来非常成功)。 我们将我们感兴趣的算法系列(加油车类型)隔离到具有多种实现的单独接口中。我们将它们与汽车的本质分开。因此,现在,如果我们需要对这个或那个加油过程进行任何更改,这不会以任何方式影响我们的汽车类别。至于可互换性,为了实现它,我们只需要向我们的类添加一个 setter 方法Auto
:
public class Auto {
FillStrategy fillStrategy;
public void fill() {
fillStrategy.fill();
}
public void gas() {
System.out.println("Едем вперед");
}
public void stop() {
System.out.println("Тормозим!");
}
public void setFillStrategy(FillStrategy fillStrategy) {
this.fillStrategy = fillStrategy;
}
}
现在我们可以即时改变策略:
public class Main {
public static void main(String[] args) {
ChildrenBuggies buggies = new ChildrenBuggies();
buggies.setFillStrategy(new StandartFillStrategy());
buggies.fill();
}
}
如果突然间儿童越野车开始加满汽油,我们的程序将为这种情况做好准备:)实际上,仅此而已!您已经学习了另一种设计模式,您无疑会需要它,并且在处理实际项目时会不止一次地帮助您:) 再见!
GO TO FULL VERSION