前言
上一次,我们用了静态工厂模式,制作了一个文件类型转换的小程序,相信大家也简单的体会到了设计模式带来的好处,但是,在静态工厂模式,我们把所有的判断全部放在抽象工厂里,这样当我们的产品越来越多时,判断也变得越来越多,十分不利于维护。为此,我们可以扩展一下思维,静态工厂模式里我们只抽象了产品,但是没有抽象工厂,我们是否可以按照这种思想,把工厂也抽象出来呢?答案显而易见,当然可以,我们把工厂也抽象出来以后,就成了这次我们要介绍的工厂方法模式。
一、工厂方法模式是什么?
工厂方法模式的定义:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
由定义可知,我们便得到了4个角色。
1.Product:抽象产品、2.ConcreteProduct:具体产品、3.Factory:抽象工厂、4.ConcreteFactory:具体工厂。
二、应用实例
我们和上次一样,还是用一个小程序来让大家感受一下工厂方法模式的实现过程。
有这样一个小需求,小弓同学想做一个快速搜索栏,因为浏览器打开后,有很多新闻或者广告弹窗,小弓同学觉得很烦,而且小弓同学作为软件工程的学生,需要经常查阅资料,他想有一个可以直接可以输入问题的文本框就好了,于是他说干就干,用他刚学会的工厂方法模式,制作了一个简单的搜索框,我们来看看他是怎么实现的吧~~
1.画类图
在编码前,画类图是必须要完成的。根据需求简单的搜索框,(我们以CSDN和B站为例),来画工厂方法模式的类图。
2.根据类图写代码
有了上次静态工厂设计模式的经验,我们可以总结出,应该先写抽象层(抽象产品、抽象工厂),再去写具体层(具体产品、具体工厂),并且产品和工厂也有先后关系,应该先写产品的代码,再写工厂的代码。好了,废话不多说,打开我们的“作案工具”idea开始造吧!!!
首先,我们先写抽象产品的抽象类。
/**
* 搜索引擎抽象类
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public abstract class Search {
/**
* 搜索的抽象方法
* @param content 搜索的内容
*/
public abstract void toSearch(String content);
}
接着,我们来写具体实现这个抽象类的子类。
import java.awt.*;
import java.net.URI;
/**
* CSDN搜索引擎类
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public class CSDNSearch extends Search {
@Override
public void toSearch(String content) {
try {
String url = "https://so.csdn.net/so/search?q=" + content; // CSDN搜索
URI uri = URI.create(url);
// 获取当前系统桌面扩展
Desktop desktop = Desktop.getDesktop();
// 判断系统桌面是否支持要执行的功能,获取系统默认浏览器打开链接
if (desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(uri);
System.out.println("亲,已为您搜索到" + content + "的所有内容!!!");
}
} catch (java.lang.NullPointerException e) {
System.out.println("亲,您的url为空:");
e.printStackTrace();
} catch (java.io.IOException e) {
System.out.println("亲,无法获取系统默认浏览器:");
e.printStackTrace();
}
}
}
import java.awt.*;
import java.net.URI;
/**
* B站搜索引擎类
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public class BSearch extends Search {
@Override
public void toSearch(String content) {
try {
String url = "https://search.bilibili.com/all?keyword=" + content; // B站搜索
URI uri = URI.create(url);
// 获取当前系统桌面扩展
Desktop desktop = Desktop.getDesktop();
// 判断系统桌面是否支持要执行的功能,获取系统默认浏览器打开链接
if (desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(uri);
System.out.println("亲,已为您搜索到" + content + "的所有内容!!!");
}
} catch (java.lang.NullPointerException e) {
System.out.println("亲,您的url为空:");
e.printStackTrace();
} catch (java.io.IOException e) {
System.out.println("亲,无法获取系统默认浏览器:");
e.printStackTrace();
}
}
}
产品这边我们就写好了,然后我们写工厂相关的代码。
首先创建一个工厂接口(不一定非要是接口,仍然使用抽象类也可以,根据具体情况,具体对待就行)。
/**
* 搜索工厂接口
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public interface SearchFactory {
/**
* 创建搜索引擎的抽象方法
* @return 搜索引擎
*/
Search createSearch();
}
然后,我们写具体的工厂,就是可以生产“CSDN”、“B站”的工厂。
/**
* CSDN搜索工厂
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public class CSDNSearchFactory implements SearchFactory {
@Override
public Search createSearch() {
return new CSDNSearch();
}
}
/**
* B站搜索工厂
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public class BSearchFactory implements SearchFactory {
@Override
public Search createSearch() {
return new BSearch();
}
}
到这里呢,我们就已经把最核心的代码写完了,但是我们还没有实现搜索框的绘制,关于搜索框的绘制,不属于设计模式里的重要内容,小弓同学就简单的在下方为大家用javaFX绘制了一个搜索框,如果觉得不美观的同学,可以自己去搜索javaFX的使用,去美化它,这里不做过多赘述。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
/**
* 搜索框类
* 作者:Cool_foolisher
* 开始日期:2023-4-7
* 结束日期:2023-4-7
*/
public class Main extends Application {
private Button button; // 搜索按钮
private Scene scene; // 场景
private TextField searchText; // 文本框
private ComboBox<String> comboBox; // 下拉框
private SearchFactory searchFactory; // 搜索引擎工厂
private Search search; // 搜索引擎
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
comboBox = new ComboBox<>();
comboBox.getItems().add("CSDN");
comboBox.getItems().add("B站");
comboBox.setStyle("-fx-font-size:19px"); // 修改字体大小
comboBox.getSelectionModel().select(0); // 修改默认显示选项
searchText = new TextField();
searchText.setFont(new Font("宋体",20));
button = new Button();
button.setText("快速搜索");
button.setFont(new Font("宋体",20));
button.setOnMouseClicked(event -> {
String searchContext = searchText.getText().trim();
String choose = comboBox.getValue();
if ("CSDN".equals(choose)) {
searchFactory = new CSDNSearchFactory();
search = searchFactory.createSearch();
search.toSearch(searchContext);
} else if ("B站".equals(choose)) {
searchFactory = new BSearchFactory();
search = searchFactory.createSearch();
search.toSearch(searchContext);
} else {
System.out.println("亲,暂不支持该搜索引擎!!!");
}
});
// 面板
FlowPane stackPane = new FlowPane();
stackPane.getChildren().add(comboBox);
stackPane.getChildren().add(searchText);
stackPane.getChildren().add(button);
// 场景
scene = new Scene(stackPane,500,42);
// 窗口
primaryStage.setTitle("小弓快速搜索框");
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
}
}
下面是程序运行时的效果图。
三、总结
以上就是工厂方法模式的全部内容,可以说工厂方法模式是静态工厂模式的扩展,顺便说一下,工厂方法模式是属于我们23种设计模式的哦,它是创建型模式里最常用的设计模式。这么厉害的它有以下优点:
1.它能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部。
2.在系统中加入新的产品时,无需修改抽象工厂和抽象产品的接口或抽象类,也无需修改其他具体工厂和具体产品,只需要添加一个具体产品和具体工厂即可,简而言之,就是易扩展,这也完全符合开闭原则。
当然,在完美的事物,也会有相应的缺点:
1.在添加新产品时,需要写新的具体产品类及对应的工厂,在一定程度上增加了系统的复杂性,给系统带来额外的开销。
2.为了提高代码的可扩展性,引入了大量的抽象层,增加了代码的抽象性和理解难度。
结束语
对于搜索功能不一定非得是“CSDN”和“B站”,有其他需要的化,可以根据自己的需求按照上述步骤去完成。如果觉得我的这篇文章对您有帮助,欢迎大家动动小手给我点赞,对于工厂方法模式有疑问的话,欢迎大家在评论留言!!!本人也是刚刚学习编程的小菜鸟,文章如有写的不对的地方,也欢迎大佬的指正!!!