Java设计模式之工厂模式学习
作者平时学习的小总结,希望可以帮到你更好了解。
本章节介绍Java类和接口的工厂模式,帮助你对工厂模式有一个新的了解,这三种工厂模式的升级基于前一代的基础上进行升级的,所以一步一步看下去效果会更好。
1.简单工厂模式
简单工厂模式就是我们把一些类(比如说某一产品汽车,有很多品种看成各种类,但他们本质上有很多相似处,本质上是汽车,把它们的相似之处看做一些方法,比如说都有那个外观、发动机情况等等)的相似方法都用一个接口来定义,然后在让这些类去完成实现这些方法,并在方法定义各自的特性,然后就是这个模式的重点就是创建一个工厂类,里边定义一个静态方法,这个静态方法就可以创建对应的类的对象(也就是汽车中奥迪这个汽车的实例化),这个方法会根据一些传入的参数来决定创建那个类的对象(具体汽车品牌),这一步完成后我们在一个操作平台上,我们通过调用工厂类的静态方法并传入你想要的产品的关键词,返回给一个前边定义的接口引用,这样我们就可以使用接口中的方法,也就是你可以看到产品的一些特性以及交互等等,这里就是为啥不用对应产品类的引用呢,因为我们引入这个接口就是为了降低对于相应类的依赖,也就是耦合性,简化客户那边的操作手段,不需要知道很多细节就可以使用想要的产品和体验,这里耦合性差不多就是依赖性,而且使用接口的话,当出现了一个新产品,直接实现方法即可,对于要修改产品的某些特性,为了便于维护,我们只需再使用一个新的类去实现接口就行,保留的原数据,方便出现差错进行维护
//假设有一个Shape接口和它的实现类Circle、Square。简单工厂类
//ShapeFactory可以根据传入的参数来创建Circle或Square的实例。
// 定义一个形状接口(Shape),包含一个draw方法,用于绘制形状。
public interface Shape {
void draw();
}
// Circle类实现了Shape接口,表示一个圆形。
// 它重写了draw方法,以输出一个表示正在绘制圆形的消息。
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
// Square类实现了Shape接口,表示一个正方形。
// 它重写了draw方法,以输出一个表示正在绘制正方形的消息。
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
// ShapeFactory是一个工厂类,用于根据提供的类型字符串创建和返回相应的形状对象。
public class ShapeFactory {
// getShape方法接受一个字符串参数shapeType,并根据该参数的值返回相应的形状对象。
// 如果shapeType为null,或者不匹配任何已知的形状类型,则返回null。
// 否则,它使用if-else语句来判断应该创建哪种类型的形状对象,并返回新创建的对象。
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
// 以下是使用ShapeFactory类的示例代码:
// 创建一个ShapeFactory对象。
ShapeFactory shapeFactory = new ShapeFactory();
// 使用getShape方法获取一个圆形对象,并将其存储在shape1变量中。
Shape shape1 = shapeFactory.getShape("CIRCLE");
// 调用shape1对象的draw方法,输出“Inside Circle::draw() method.”。
shape1.draw();
// 使用getShape方法获取一个正方形对象,并将其存储在shape2变量中。
Shape shape2 = shapeFactory.getShape("SQUARE");
// 调用shape2对象的draw方法,输出“Inside Square::draw() method.”。
shape2.draw();
以下就是这段代码类与接口的关系示意图
2.工厂方法模式
这个就是为了解决简单工厂模式的工厂类的静态方法,当有新的产品加入,则需要把工厂类的静态方法新增该对象的创建情况,所以为了便于维护,因为这个方法创建对应对象的,所以把这个工厂类可以变为工厂接口(里边定义一个创建对象的方法),然后每个实际类的工厂类去实现这个接口的方法,这样以后有新的产品加入,直接让这个产品的工厂类去实现接口即可,这样得到了维护,那么我们在使用时只需把那个对应对象的工厂类创建对象然后返回给工厂接口引用,然后通过调用该创建对象方法返回给产品接口引用,在通过这个产品接口引用去调用产品相关的方法得到特性以及交互即可
//以日志记录器为例,假设有一个Logger接口和它的实现类FileLogger、
//ConsoleLogger。现在创建一个工厂接口LoggerFactory和它的实现类。
// 定义一个日志记录接口 Logger,该接口包含一个 writeLog 方法用于写入日志。
public interface Logger {
void writeLog();
}
// FileLogger 类实现了 Logger 接口,代表将日志写入到文件。
// 重写了 writeLog 方法以打印消息“Writing to file”。
public class FileLogger implements Logger {
@Override
public void writeLog() {
System.out.println("Writing to file");
}
}
// ConsoleLogger 类实现了 Logger 接口,代表将日志写入到控制台。
// 重写了 writeLog 方法以打印消息“Writing to console”。
public class ConsoleLogger implements Logger {
@Override
public void writeLog() {
System.out.println("Writing to console");
}
}
// 定义一个 LoggerFactory 接口,用于创建 Logger 对象。
// 包含一个 createLogger 方法用于创建和返回 Logger 类型的对象。
public interface LoggerFactory {
Logger createLogger();
}
// FileLoggerFactory 类实现了 LoggerFactory 接口,它用于创建并返回 FileLogger 类型的对象。
// 重写了 createLogger 方法以返回一个新创建的 FileLogger 对象。
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger();
}
}
// ConsoleLoggerFactory 类实现了 LoggerFactory 接口,它用于创建并返回 ConsoleLogger 类型的对象。
// 重写了 createLogger 方法以返回一个新创建的 ConsoleLogger 对象。
public class ConsoleLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new ConsoleLogger();
}
}
// 使用示例:
// 创建一个 FileLoggerFactory 对象,并通过调用它的 createLogger 方法获取一个 FileLogger 对象。
// 随后,通过调用这个 Logger 对象的 writeLog 方法,向文件写入日志(在本例中实际上是打印了一条消息)。
LoggerFactory factory = new FileLoggerFactory();
Logger logger = factory.createLogger();
logger.writeLog();
// 若要使用 ConsoleLogger,只需要更改工厂类实例化的行,例如:
// LoggerFactory factory = new ConsoleLoggerFactory();
// 其他代码无需变动。
以下就是这段代码类与接口的关系示意图
3.抽象工厂模式
这个在实现过程中与工厂方法抽象模型是一致的,意思上差不多的,在实际应用场景有区别,工厂方法模型主要是创建一个产品中的各种品牌(比如产品为汽车,有奥迪、宝马、五菱等不同品牌,但都是汽车),而抽象工厂模型不仅单指产品中的品牌而且还要有一个产品的组成(汽车的发动机、底盘等等对象组成),也可以这样理解,工厂方法模型就是创建一个名字不同的人也就是对应不同类的创建,而抽象工厂模型不仅是名字不同(不同类),而且还得要有他的组成部分(如嘴巴、眼睛、),每个不同类都有相同产品组成,把这些组成部分定义成接口方法,然后让不同品牌(类)去实现这些接口,然后工厂接口就会创建相关对象的创建方法,然后通过具体的工厂类实现接口,客户端在使用时,首先创建工厂类构造器返回给工厂接口引用,然后在调用这个工厂引用调用相关的方法去调用相关创建对象的方法,然后就得到对于产品的一部分去交互使用,大概就这些了
//以图形界面组件为例,有按钮(Button)和文本框(TextField)两种组件,
//每种组件都有不同风格(如Windows和Mac)。抽象工厂模式可以用来创建这些
//风格一致的组件集合。
// 定义了一个GUIFactory接口,用于创建GUI组件的工厂
public interface GUIFactory {
// 创建一个按钮
public Button createButton();
// 创建一个文本框
public TextField createTextField();
}
// 定义了按钮的接口,所有按钮都应实现这个接口
public interface Button {
// 绘制按钮的方法
public void paint();
}
// 定义了文本框的接口,所有文本框都应实现这个接口
public interface TextField {
// 绘制文本框的方法
public void paint();
}
// WinButton类实现了Button接口,代表Windows风格的按钮
public class WinButton implements Button {
// 实现paint方法,输出使用的是WinButton
@Override
public void paint() {
System.out.println("You are using WinButton.");
}
}
// WinTextField类实现了TextField接口,代表Windows风格的文本框
public class WinTextField implements TextField {
// 实现paint方法,输出使用的是WinTextField
@Override
public void paint() {
System.out.println("You are using WinTextField.");
}
}
// MacButton类实现了Button接口,代表Mac风格的按钮
public class MacButton implements Button {
// 实现paint方法,输出使用的是MacButton
@Override
public void paint() {
System.out.println("You are using MacButton.");
}
}
// MacTextField类实现了TextField接口,代表Mac风格的文本框
public class MacTextField implements TextField {
// 实现paint方法,输出使用的是MacTextField
@Override
public void paint() {
System.out.println("You are using MacTextField.");
}
}
// WinFactory类实现了GUIFactory接口,用于创建Windows风格的GUI组件
public class WinFactory implements GUIFactory {
// 实现createButton方法,返回WinButton实例
@Override
public Button createButton() {
return new WinButton();
}
// 实现createTextField方法,返回WinTextField实例
@Override
public TextField createTextField() {
return new WinTextField();
}
}
// MacFactory类实现了GUIFactory接口,用于创建Mac风格的GUI组件
public class MacFactory implements GUIFactory {
// 实现createButton方法,返回MacButton实例
@Override
public Button createButton() {
return new MacButton();
}
// 实现createTextField方法,返回MacTextField实例
@Override
public TextField createTextField() {
return new MacTextField();
}
}
// 使用示例:
// 创建一个Windows风格的GUI工厂
GUIFactory factory = new WinFactory();
// 使用工厂创建一个按钮
Button button = factory.createButton();
// 使用工厂创建一个文本框
TextField textField = factory.createTextField();
// 绘制按钮,输出“You are using WinButton.”
button.paint();
// 绘制文本框,输出“You are using WinTextField.”
textField.paint();
以下就是这段代码类与接口的关系示意图
我有一个想法就是对于抽象工厂模式创建一系列相关的对象,其实这本来是这个类的组成,可以封装到这个类,但是这样又不行了,这样的强制转换才能使用原对象自己定义的方法或属性,所以把这些通过接口封装,然后无需知道是哪个类的对象就可以进行相关的交互,是这样吗(这是我一个自己的思考,下边是来源于网络)
抽象工厂模式的关键在于它提供了一种方式来封装和创建一系列相关的对象,这些对象通常是一起使用的,构成了一个“产品族”。通过将这些对象的创建逻辑封装在工厂类中,我们可以避免在客户端代码中直接使用具体类,从而减少了代码之间的耦合度。
确实,如果我们将这些相关的对象直接封装到一个类中,那么在使用这些对象时,我们可能需要进行强制类型转换才能访问它们特定的方法或属性。这样做不仅增加了代码的复杂性,还破坏了封装性,使得代码更加难以维护和扩展。
而通过使用接口来封装这些对象,我们可以实现一种更加灵活和可扩展的设计。接口定义了一组规范,规定了对象应该具备的行为。不同的具体类可以实现同一个接口,从而提供不同的实现方式。这样,客户端代码就可以通过接口来与这些对象进行交互,而无需关心它们的具体类。
在抽象工厂模式中,我们通常定义一组接口来表示产品族中的不同类型的产品。然后,具体的工厂类负责实现这些接口,并提供创建相应产品对象的方法。客户端代码通过调用工厂类的方法来获取产品对象,并通过接口来与它们进行交互。
这种设计方式的好处是,它使得客户端代码与具体类之间的依赖关系降低到了最低限度。当需要添加新的产品族或新的产品时,我们只需要定义新的接口和实现类,而无需修改现有的客户端代码。这样,我们的代码就更加灵活、可扩展和可维护了。
4.总结
根据实际场景去判断使用哪个工厂模式
主要是这里的接口为了降低耦合,也就是不需要知道其中的实现类,无需了解其中的细节,可以使用户更便捷使用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?