java设计模式-工厂模式
Java 中的工厂模式是设计模式的一种,主要用于管理对象的创建。它帮助将对象的实例化逻辑从使用对象的逻辑中分离出来,使得代码更加模块化,增加了代码的灵活性和可维护性。工厂模式主要有三种变体:简单工厂模式、工厂方法模式和抽象工厂模式。
文章目录
概念
Java 中的工厂模式是设计模式的一种,主要用于管理对象的创建。它帮助将对象的实例化逻辑从使用对象的逻辑中分离出来,使得代码更加模块化,增加了代码的灵活性和可维护性。工厂模式主要有三种变体:简单工厂模式、工厂方法模式和抽象工厂模式。
一、简单工厂模式
简单工厂模式并不是一个真正的设计模式,但是它是工厂方法模式的一个简化版本。它有一个中心化的工厂类,负责创建其他类的实例。客户端通过传递类型信息给工厂,来获取所需的对象实例。简单工厂模式适合产品种类较少且不会频繁增加的场景,在这种情况下,可以通过简单工厂模式简化对象的创建过程,同时保持客户端和具体产品解耦。
1、角色和职责
角色
- 工厂类(Factory Class):这是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的职责是提供一个创建对象的方法,客户端通过调用这个方法来创建对象。这个方法通常根据传递给它的参数来决定创建哪种类型的对象。
- 抽象产品(Abstract Product):这是一个接口或抽象类,定义了产品的公共接口。在具体实现类中,这些接口或抽象类的方法将被实现。
- 具体产品(Concrete Product):继承或实现抽象产品的类,工厂类将创建并返回这些具体产品的实例。每个具体产品都会实现抽象产品定义的接口,表示不同类型的产品对象。
职责
- 工厂类的职责:
- 根据客户端的请求,决定创建哪种具体产品的实例。
- 隐藏了创建产品的具体逻辑,客户端不需要知道具体产品类的名字,只需要知道工厂类。
- 可能包含选择合适产品类并创建其实例的逻辑。
- 抽象产品的职责:
- 声明所有具体产品都应实现的接口,定义了产品的规范,确保所有具体产品的一致性。
- 抽象产品的职责:
- 实现或继承抽象产品的接口或类,定义具体产品的具体属性和方法。
- 每个具体产品都将提供不同的实现,这些实现通过工厂类创建并返回给客户端。
2、优点
- 帮助实现了代码的解耦,客户端不需要直接创建对象,而是通过工厂类来做这件事,减少了客户端和产品对象之间的依赖。
- 添加新的产品类时,只需要扩展工厂类即可,无需修改已有的客户端代码,提高了系统的可维护性。
3、缺点
- 当添加新产品时,需要修改工厂类,违反了开闭原则。
- 工厂类的职责过重,随着产品种类的增加,工厂方法会变得越来越复杂。
4、适用场景
- 创建的对象数量不多且不会频繁变化。
- 客户端不关心对象的创建过程。
5、示例1
- 定义一个接口或者抽象类(Product):所有具体的产品实现这个接口或继承这个抽象类,这些产品在概念上是相关的,但实现细节不同。
- 创建具体的产品类(Concrete Products):实现或继承第一重定义接口或抽象类。每个具体产品都包含特定于产品的实现代码。
- 创建工厂(Factory):包含一个或多个方法,这些方法用于根据输入参数的不同,决定创建并返回哪一种具体产品的实例。客户端调用这个方法而不是直接创建产品对象。
- 客户端调用工厂类的方法:客户端使用工厂类,而不是直接实例化产品对象。客户端不需要知道如何创建这些对象,只需知道工厂方法的参数。
假设我们有一个应用,需要根据用户的需求创建不同类型的日志记录器,比如文件日志记录器和数据库日志记录器。
第一步:定义产品接口
public interface Logger {
void log(String message);
}
第二步:创建具体产品类
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("Logging message to a file: " + message);
}
}
public class DatabaseLogger implements Logger {
public void log(String message) {
System.out.println("Logging message to the database: " + message);
}
}
第三步:创建工厂类
public class LoggerFactory {
public static Logger getLogger(String type) {
if (type.equalsIgnoreCase("file")) {
return new FileLogger();
} else if (type.equalsIgnoreCase("database")) {
return new DatabaseLogger();
} else {
throw new IllegalArgumentException("Unknown logger type");
}
}
}
第四步:客户端调用工厂类的方法
public class Client {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger("file");
logger.log("This is a message.");
logger = LoggerFactory.getLogger("database");
logger.log("This is another message.");
}
}
6、示例2
步骤1:创建产品接口和具体产品类,首先定义一个产品接口FileViewer
和几个具体的产品类TextViewer
、ImageViewer
、VideoViewer
,每个产品类实现 FileViewer
接口。
interface FileViewer {
void display();
}
class TextViewer implements FileViewer {
public void display(){
System.out.println("Display text file...");
}
}
class ImageViewer implements FileViewer {
public void display(){
System.out.println("Display image file...");
}
}
class VideoViewer implements FileViewer {
public void display(){
System.out.println("Display video file...");
}
}
步骤2:创建一个简单工厂类ViewerFactory
,它有一个静态方法 createViewer
,根据文件类型返回对应的视图对象。
class ViewerFactory {
public static FileViewer createViewer(String fileType){
switch(fileType){
case: "text"
return new TextViewer();
case: "image"
return new ImageViewer();
case: "video"
return new VideoViewer();
default:
throw new IllegalArgumentException("Unsupported file type:" + fileType);
}
}
}
步骤3:客户端使用工厂类
public class FactoryClient{
public static void main(String[] args){
FileViewer viewer = ViewerFactory.createViewer("text");
viewer.display();
viewer = ViewerFactory.createViewer("image");
viewer.display();
}
}
7、示例3
public interface ICar {
void drive();
}
public class Bmw implements ICar {
public void drive(){
System.out.println("Bmw");
}
}
public class Audi implements ICar{
public void drive(){
// audi
}
}
public class Benz implements ICar{
public void drive(){
// benz
}
}
public class SimpleFactory{
private SimpleFactory(){ }
public static ICar createCar(String carType){
if("BMW".equalsIgnoreCase(carType)){
return new Bmw();
}else if ("Audi".equalsIgnoreCase(carType)){
return new Audi();
}else if ("Benz".equalsIgnoreCase(carType)){
return new Benz();
}else {
return null;
}
}
}
public class FactoryClient{
public static void main(String[] args){
SimpleFactory.create("BMW").drive();
}
}
8、示例4:枚举
public enum EnumCarFactory{
BMW{
@Override
public ICar create(){
return new Bmw();
}
},
AUDI{
@Override
public ICar create(){
return new Audi();
}
},
BENZ{
@Override
public ICar create(){
return new Benz();
}
}
// abstarct 修饰,强制每个枚举实现该方法
public abstarct ICar create();
}
public class FactoryClient{
public static void main(String[] args){
EnumCarFactory.AUDI.create().drive();
}
}
9、示例5:反射
public class EnhancedSimpleFactory{
private EnhancedSimpleFactory() {}
public static <T> T create(Class<? extend T> clazz){
T obj = null;
try {
obj = (T) Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e){
e.printStackTrace();
}
return obj;
}
}
public class FactoryClient{
public static void main(String[] args){
// 入参类路径也可来自配置文件、数据库等,因此更具灵活性。
EnhancedSimpleFactory.create(Benz.class).drive()
}
}
二、工厂方法模式
工厂方法模式是设计模式中的一种,属于创建型模式的一部分。它定义了一个创建对象的接口,但是让实现这个接口的类来决定实例化哪一个类。工厂方法让类的实例化延迟到其子类进行。
1、角色和职责
角色
- 抽象产品(Abstract Product):这是一个接口或抽象类,定义了产品的公共接口。所有的产品类都需要实现这个接口,这样的设计使得所有产品类在概念上是相同的。
- 具体产品(Concrete Product):实现或继承自抽象产品的类。每个具体产品都会定义具体的业务操作,这些是客户端所期望的功能。
- 抽象工厂(Creator):这是一个接口或抽象类,声明了工厂方法,这个方法返回一个抽象产品类型。抽象工厂使得创建对象的实现延迟到其子类中进行。
- 具体工厂(Concrete Creator):继承或实现抽象工厂的类。具体工厂负责创建一个或多个具体产品的实例,决定实例化哪个产品类。每个具体工厂都必须实现抽象工厂定义的工厂方法。
职责
- 抽象产品(Product)的职责:
- 定义所有具体产品必须实现的接口,确保所有产品类在概念上是一致的,提供了产品实例的通用属性和方法。
- 具体产品(Concrete Product)的职责:
- 实现抽象产品接口定义的操作,代表特定实现的产品对象。
- 抽象工厂(Creator)的职责:
- 声明工厂方法,返回一个产品实例。工厂方法通常是抽象的,需要子类实现。
- 可以提供工厂方法的默认实现,返回一个默认的产品对象。
- 具体工厂(Concrete Creator)的职责:
- 重写或实现抽象工厂中定义的工厂方法,以创建和返回具体产品的实例。具体工厂知道应当实例化哪一个具体产品类。
- 直接与客户端代码交互,提供创建产品的具体实现。
2、优点
- 提供了代码解耦:在工厂方法模式中,客户端不需要知道它所创建实例的类的具体类名,只需知道对应的工厂即可。这有助于系统在不修改客户端代码的情况下引入新的类,提高了系统的灵活性。
- 扩展性高:添加新的产品类不需要修改已有的工厂类,只需添加相应的具体产品类和对应的具体工厂即可,符合开闭原则。
- 强制隔离了创建和使用代码:用户只关心产品的接口而不关心具体类的实现,工厂方法也使得用户代码和具体类的实现解耦。
- 提供了一种扩展的策略,比简单工厂模式更有弹性。
3、缺点
- 可能导致类的数量增加:每增加一个产品,都需要增加一个具体产品类和一个具体工厂类,这会导致系统类的数量成倍增加,增加了系统的复杂性。
- 系统的抽象性和理解难度增加:需要引入抽象层,在客户端代码中既包含抽象也包含具体类的操作,对系统结构和抽象层的理解将会更加复杂。
4、使用场景
- 当一个类不知道它所必须创建的对象的类的时候:在工厂方法模式中,类的实例化延迟到其子类。
- 当一个类希望由它的子类来指定创建对象的时候:工厂方法使类的实例可以在运行时更加灵活地增加新的类型。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪个帮助子类是委托者这一信息局部化时。
5、示例1
考虑一个日志记录器的例子,我们需要根据不同的场景(例如文件日志记录和数据库日志记录)创建不同类型的日志记录器。
步骤 1: 定义产品接口
public interface Logger {
void log(String message);
}
步骤 2: 创建具体产品类
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("File logger: " + message);
}
}
public class DatabaseLogger implements Logger {
public void log(String message) {
System.out.println("Database logger: " + message);
}
}
步骤 3: 定义工厂接口
public interface LoggerFactory {
Logger createLogger();
}
步骤 4: 创建具体工厂类
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
// 可以在这里添加创建FileLogger前的逻辑,如配置加载等
return new FileLogger();
}
}
public class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
// 可以在这里添加创建DatabaseLogger前的逻辑,如配置加载等
return new DatabaseLogger();
}
}
步骤 5: 客户端代码
public class FactoryMethodDemo {
public static void main(String[] args) {
LoggerFactory factory;
Logger logger;
// 使用文件日志记录器
factory = new FileLoggerFactory();
logger = factory.createLogger();
logger.log("This is a file log message.");
// 使用数据库日志记录器
factory = new DatabaseLoggerFactory();
logger = factory.createLogger();
logger.log("This is a database log message.");
}
}
这个示例中,**LoggerFactory**
是工厂接口,**FileLoggerFactory**
和 **DatabaseLoggerFactory**
是具体的工厂类,它们负责创建对应的产品类实例。客户端代码仅与接口 **LoggerFactory**
和 **Logger**
交互,具体使用哪个日志记录器类的决定权交给了具体的工厂类,这样就实现了创建逻辑与使用逻辑的分离,提高了代码的灵活性和扩展性。
三、抽象工厂模式
抽象工厂模式是一种创建型设计模式,它提供了一种方式,可以封装一组具有共同主题的单独的工厂而无需指定它们的具体类。这种模式是围绕一个超级工厂创建其他工厂的概念。该超级工厂又称为其他工厂的工厂。在抽象工厂模式中,接口是负责创建相关对象的工厂,不需要明确指定它们的类。
1、角色和职责
角色
- 抽象工厂(Abstract Factory):提供一个接口,用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。
- 具体工厂(Concrete Factory):实现抽象工厂接口的具体类。每个具体工厂都能够按照工厂接口定义的方式生产一组具体产品。不同的具体工厂对应于不同的产品变体。
- 抽象产品(Abstract Product):为一系列产品对象声明一个接口。在抽象接口中声明了所有产品必须实现的操作。
- 具体产品(Concrete Product):抽象工厂模式所创建的实际产品对象,每个具体产品都是一个抽象产品的实现。
- 客户端(Client):仅使用由抽象工厂和抽象产品提供的接口声明的方法。客户端通过抽象接口操纵实例,从而使得与应用程序的具体类解耦。
职责
- 抽象工厂(Abstract Factory)的职责:
- 声明了一组用于创建一系列相关或相互依赖对象的方法,每个方法对应于一种产品。
- 具体工厂(Concrete Factory)的职责:
- 实现抽象工厂的方法来创建具体的产品对象。每个具体工厂对应于特定的产品变体,并创建具有特定实现的产品对象。
- 抽象产品(Abstract Product)的职责:
- 为一类产品声明接口。在该接口中声明了所有具体产品都必须实现的操作。
- 具体产品(Concrete Product)的职责:
- 实现抽象产品接口的具体类。定义了一个将被相应的具体工厂创建的产品对象。
- 客户端(Client)的职责:
- 仅调用从抽象工厂和抽象产品类中派生的接口。客户端通过这些接口与工厂创建的对象交互,从而使具体的工厂和产品类与客户端代码解耦。
2、优点
- 分离接口和实现:客户端代码通过接口操作实例,从而解耦了客户端和具体类的依赖。
- 增加新的具体工厂和产品族很方便:无需修改现有系统,符合开闭原则,系统的可扩展性好。
- 强化了程序的抽象层次:抽象工厂模式通过使用产品族,能够更方便地进行替换产品系列,增加了程序的灵活性和可维护性。
3、缺点
- 难以支持新种类的产品:如果需要添加新产品,则必须修改抽象工厂的接口,这将涉及到抽象工厂类及其所有子类的修改,违背了开闭原则。
- 增加了系统的抽象性和复杂度:引入了多层接口和类,系统的学习和理解难度增加。
4、适用场景
- 当系统中的产品有多于一个的产品族,而系统只消费其中某一族的产品时:抽象工厂可以为访问某一产品族的产品提供接口。
- 当一个产品族中的多个对象被设计成一起工作时,它可以保证客户端始终只使用同一个产品族中的对象。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
5、示例1
假设我们正在开发一个跨平台的UI库,我们需要为不同的操作系统(如Windows和Mac)创建不同风格的UI元素(如按钮和复选框)。我们将使用抽象工厂模式来解决这个问题。
步骤 1: 创建抽象产品类和具体产品类
public interface Button {
void paint();
}
public class WinButton implements Button {
public void paint() {
System.out.println("Render a button in Windows style");
}
}
public class MacButton implements Button {
public void paint() {
System.out.println("Render a button in MacOS style");
}
}
public interface Checkbox {
void paint();
}
public class WinCheckbox implements Checkbox {
public void paint() {
System.out.println("Render a checkbox in Windows style");
}
}
public class MacCheckbox implements Checkbox {
public void paint() {
System.out.println("Render a checkbox in MacOS style");
}
}
步骤 2: 创建抽象工厂类和具体工厂类
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
public class WinFactory implements GUIFactory {
public Button createButton() {
return new WinButton();
}
public Checkbox createCheckbox() {
return new WinCheckbox();
}
}
public class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
步骤 3: 客户端代码
public class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void paint() {
button.paint();
checkbox.paint();
}
}
public class Demo {
public static void main(String[] args) {
GUIFactory factory = new WinFactory();
Application app = new Application(factory);
app.paint();
factory = new MacFactory();
app = new Application(factory);
app.paint();
}
}
在这个例子中,GUIFactory是一个抽象工厂,负责定义创建UI元素的接口。WinFactory和MacFactory是具体的工厂,实现了GUIFactory接口,分别创建Windows风格和Mac风格的UI元素。客户端代码依赖于抽象工厂和抽象产品的接口,从而可以在不关心具体产品细节的情况下,使用不同的产品族。这就是抽象工厂模式的强大之处。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)