下面是一个没有使用工厂设计模式的例子,ResourceLoader
类负责根据 URL 的前缀来加载资源。根据不同的前缀,它执行不同的操作来创建 Resource
对象。这导致了以下问题:
Resource
对象,这造成了代码的重复。ResourceLoader
类的代码,并增加对应的分支。这违反了开闭原则,使代码难以扩展和维护。ResourceLoader
类的职责既包括根据前缀选择创建资源对象的逻辑,又包括资源对象的创建过程,导致职责不够单一。没有使用工厂设计模式的代码实现不够灵活,代码复用性较低,并且随着业务需求的变化,代码的维护和扩展会变得困难。相比之下,使用工厂设计模式可以将对象的创建逻辑封装到工厂类中,提高了代码的可扩展性和可维护性,同时使得代码更加清晰和易于理解。
public class Resource {
private String url;
public Resource(String url) {
this.url = url;
}
}
public class ResourceLoader {
private Resource load(String url) {
// 根据前缀匹配
String prefix = getPrefix(url);
if (prefix.equals("http")) {
// 很复杂的操作,可能此处会执行很久
return new Resource(url);
} else if (prefix.equals("file")) {
// 很复杂的操作,可能此处会执行很久
return new Resource(url);
} else if (prefix.equals("classpath")) {
// 很复杂的操作,可能此处会执行很久
return new Resource(url);
} else {
return new Resource("default");
}
}
private String getPrefix(String url) {
if (url == null || "".equals(url) || url.contains(":")) {
throw new ResourceLoadException("url不合法");
}
String[] split = url.split(":");
return split[0];
}
}
上述代码包含两个类:Resource
和 ResourceLoader
。
Resource
类表示资源对象,具有一个私有的 url
字段和一个公共的构造函数。它通过构造函数接收一个 url
参数,并将其赋值给 url
字段。ResourceLoader
类是一个资源加载器,它有一个私有的 load
方法用于加载资源。该方法接收一个 url
参数,根据其前缀进行匹配。根据前缀的不同,它执行不同的操作来创建并返回一个 Resource
对象。
load
方法中,它通过调用私有的 getPrefix
方法来获取 url
的前缀。然后,使用条件语句判断前缀是什么,并在每个分支中执行复杂的操作(可能需要执行很久)来创建一个对应的 Resource
对象。如果前缀不匹配任何条件,则创建一个默认的 Resource
对象。getPrefix
方法用于从 url
中提取前缀。它首先检查 url
是否为空、空字符串或包含冒号。如果是,则抛出一个 ResourceLoadException
异常,表示 url
不合法。否则,它通过将 url
使用冒号进行分割,并返回分割后的第一个元素作为前缀。总结:以上代码展示了一个简单的资源加载器,根据给定的 url
和其前缀,创建并返回对应的 Resource
对象。它通过条件语句进行判断和创建,没有使用工厂设计模式。然而,这种实现方式存在代码冗余和可扩展性差的问题。工厂设计模式可以用来改善这些问题。
总结就是:一个工厂负责生产多个产品
简单工厂设计模式(Simple Factory Pattern)是一种创建型设计模式,旨在通过一个工厂类来封装对象的创建过程。它属于最基本的工厂模式,虽然并不是一种正式的设计模式,但在实际开发中被广泛应用。
public class ResourceLoadFactory {
public static Resource create(String type, String url) {
if (type.equals("http")) {
//很复杂的操作,可能此处会执行很久
return new Resource(url);
} else if (type.equals("file")) {
//很复杂的操作,可能此处会执行很久
return new Resource(url);
} else if (type.equals("classpath")) {
//很复杂的操作,可能此处会执行很久
return new Resource(url);
} else {
return new Resource("default");
}
}
}
其中ResourceLoadFactory
充当了工厂类的角色,create
方法充当了工厂方法。根据传入的type
参数,create
方法决定实例化并返回不同类型的Resource
对象。
简单工厂模式的优点之一是将对象的创建过程集中在一个工厂类中,使得客户端代码与具体的对象创建逻辑解耦。客户端只需要调用工厂方法,并传入相应的参数即可获得所需的对象实例,而无需了解对象的具体创建细节。
简单工厂设计模式的优点:
封装对象的创建过程:简单工厂模式将对象的创建逻辑封装在工厂类中,客户端只需调用工厂方法,而无需了解对象的具体创建过程。
解耦客户端和具体对象:客户端代码只与工厂类打交道,无需直接与具体对象类交互,从而实现了客户端代码与具体对象的解耦。
简化客户端代码:客户端只需调用工厂方法并传入相应参数,即可获得所需对象的实例,无需自行创建对象,从而简化了客户端代码。
简单工厂设计模式的缺点:
不符合开闭原则:当需要添加新的产品类型时,需要修改工厂类的代码,违反了开闭原则。这可能导致工厂类的代码过于复杂,且对于每个新的产品类型的添加都需要修改工厂类,增加了代码的维护成本。
可能过于臃肿:随着产品类型的增加,工厂类可能变得庞大而臃肿,对于复杂的产品类型结构,简单工厂模式可能无法很好地管理和创建对象。
不支持扩展性:由于工厂类集中了所有对象的创建逻辑,如果需要对不同类型的对象采取不同的创建逻辑,则无法通过简单工厂模式实现。
总结:简单工厂模式适用于创建对象的过程相对简单且不频繁变化的情况。它提供了一种简单的对象创建方式,使得客户端代码与具体对象的创建逻辑解耦。然而,它的缺点是不符合开闭原则,难以扩展和维护。在面对复杂的对象结构和频繁变化的创建逻辑时,可以考虑使用其他更灵活的创建型设计模式。
总结就是:一个工厂生产一个产品
工厂方法设计模式(Factory Method Pattern)是一种创建型设计模式,用于解决对象的创建问题。它将对象的创建延迟到子类中,通过定义一个创建对象的接口,让子类决定实例化哪个具体类。
工厂方法模式通常包含以下角色:
抽象产品(Abstract Product):定义产品的共同接口或抽象类,具体产品需要实现这个接口或继承这个抽象类。
具体产品(Concrete Product):实现抽象产品接口或继承抽象产品类,是工厂方法所创建的对象实例。
抽象工厂(Abstract Factory):定义创建产品的接口,包含创建产品的抽象方法。
具体工厂(Concrete Factory):实现抽象工厂接口,实现创建具体产品的方法。
工厂方法模式通过引入抽象工厂和具体工厂来解决直接实例化具体产品的问题。客户端代码只需要与抽象工厂进行交互,而不需要直接与具体产品类打交道。具体的产品对象由具体工厂类来创建,具体工厂类的选择由客户端来决定。
抽象产品和具体产品:
@Data
public abstract class AbstractResource {
private String url;
public AbstractResource() {
}
public AbstractResource(String url) {
this.url = url;
}
//子类都要实现的方法
public abstract InputStream getInputStream();
}
public class FileResource extends AbstractResource {
public FileResource() {
super();
}
public FileResource(String url) {
super(url);
}
@Override
public InputStream getInputStream() {
return null;
}
}
抽象工厂和具体工厂:
public interface IResourceFactory {
public AbstractResource create(String url);
}
public class FileResourceFactory implements IResourceFactory {
@Override
public AbstractResource create(String url) {
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
return new FileResource(url);
}
}
测试:
@Slf4j
public class ResourceLoaderMethod {
private IResourceFactory resourceFactory;
private static Map<String, IResourceFactory> resourceFactoryCache = new HashMap<>(8);
static {
//在类加载的时候/起一个定时器,定时读取配置文件
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("resourceFactory.properties");
Properties properties = null;
try {
properties = new Properties();
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
Iterator<Map.Entry<Object, Object>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> next = iterator.next();
String prefixKey = next.getKey().toString();
String className = next.getValue().toString();
Class<?> clazz = null;
try {
clazz = Class.forName(className);
IResourceFactory instance = (IResourceFactory) clazz.getConstructor().newInstance();
resourceFactoryCache.put(prefixKey, instance);
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
private AbstractResource load(String url) {
String prefix = getPrefix(url);
return resourceFactoryCache.get(prefix).create(url);
}
private String getPrefix(String url) {
if (url == null || "".equals(url) || url.contains(":")) {
return "default";
}
String[] split = url.split(":");
return split[0];
}
public static void main(String[] args) {
ResourceLoaderMethod rlm = new ResourceLoaderMethod();
AbstractResource load = rlm.load("file:user/inter");
System.out.println("load.getUrl() = " + load.getUrl());
}
}
这段代码展示了使用抽象工厂设计模式的资源加载器实现。
抽象产品和具体产品部分:
AbstractResource
是抽象产品类,它包含了一个 url
字段,并定义了一个抽象方法 getInputStream()
。FileResource
是具体产品类,它继承自 AbstractResource
,实现了抽象方法 getInputStream()
。抽象工厂和具体工厂部分:
IResourceFactory
是抽象工厂接口,它定义了一个方法 create()
,用于创建抽象产品 AbstractResource
对象。FileResourceFactory
是具体工厂类,实现了 IResourceFactory
接口。在 create()
方法中,它通过复杂的逻辑创建一个 FileResource
对象,并将其返回。测试部分:
ResourceLoaderMethod
类中包含了一个静态的 resourceFactoryCache
缓存,用于存储不同前缀对应的具体工厂对象。resourceFactory.properties
,获取了具体工厂类的映射关系,并将其实例化放入缓存中。load()
方法根据传入的 URL 获取前缀,并从 resourceFactoryCache
缓存中获取对应的具体工厂对象,然后调用其 create()
方法创建抽象产品对象。getPrefix()
方法用于从 URL 中提取前缀,如果 URL 为空、空字符串或包含冒号,则返回默认前缀。main()
方法中,创建 ResourceLoaderMethod
对象,并通过调用 load()
方法加载资源。最后打印出加载的资源的 URL。通过这段代码,资源加载器实现了抽象工厂设计模式。客户端只需与抽象工厂接口 IResourceFactory
进行交互,通过调用其 create()
方法,由具体工厂类根据传入的 URL 创建相应的具体产品对象。这样可以实现客户端与具体产品类的解耦,提高了代码的灵活性和可维护性。
工厂方法模式(Factory Method Design Pattern)的优点:
符合开闭原则:工厂方法模式通过将对象的创建委托给子类,实现了对扩展开放、对修改关闭。通过添加新的具体工厂子类,可以轻松地新增产品类型,而无需修改现有的代码。
提供了灵活性:工厂方法模式将对象的创建委托给子类,使得客户端可以通过选择不同的具体工厂子类来获取不同类型的对象实例。这提供了灵活性,使得系统能够根据需求动态地创建所需的对象。
封装了对象的创建逻辑:工厂方法模式将对象的创建逻辑封装在具体工厂子类中,使得客户端代码与具体对象的创建过程解耦。客户端只需关心使用工厂方法获得对象,无需关心对象的具体创建细节。
可以应对复杂的对象创建逻辑:工厂方法模式适用于需要根据不同的条件或参数来创建对象的情况。每个具体工厂子类可以实现自己特定的对象创建逻辑,使得系统能够处理复杂的对象创建需求。
工厂方法模式的缺点:
类的数量增加:引入工厂方法模式会增加系统中类的数量,每个具体产品对应一个具体工厂类,这可能会增加代码的复杂性和理解的难度。
客户端需要知道具体工厂类:客户端需要知道具体工厂类才能创建相应的对象,这增加了客户端代码的依赖性。如果客户端不知道具体工厂类,需要进一步结合其他模式(如抽象工厂模式)来解决这个问题。
总结:工厂方法模式提供了一种灵活的对象创建机制,能够根据具体需求选择不同的具体工厂子类来创建对象。它符合开闭原则,可以应对复杂的对象创建逻辑。然而,它可能会增加系统中的类数量,并要求客户端知道具体工厂类。根据具体情况,可以考虑使用工厂方法模式来实现灵活、可扩展的对象创建。
总结就是:一个工厂生产一组产品线
抽象工厂设计模式(Abstract Factory Pattern)是一种创建型设计模式,用于创建一系列相关或相互依赖的对象。它提供了一个抽象工厂接口,允许客户端使用抽象接口创建一组相关的产品对象,而无需关心具体的产品类。
抽象工厂模式通常包含以下角色:
抽象工厂(Abstract Factory):定义创建一族产品的抽象接口。它通常包含多个用于创建不同产品的抽象方法。
具体工厂(Concrete Factory):实现抽象工厂接口,负责创建一族具体产品对象。
抽象产品(Abstract Product):定义产品的共同接口或抽象类。具体产品类需要实现这个接口或继承这个抽象类。
具体产品(Concrete Product):实现抽象产品接口或继承抽象产品类,是具体工厂所创建的对象实例。
抽象工厂模式通过引入抽象工厂和具体工厂来解决直接实例化具体产品的问题。客户端代码只需要与抽象工厂进行交互,而不需要直接与具体产品类打交道。具体的产品对象由具体工厂类来创建,具体工厂类的选择由客户端来决定。
抽象产品和具体产品:
public interface IResource {
//子类都要实现的方法
InputStream getInputStream();
}
@Data
public abstract class AbstractVideoResource implements IResource {
private String url;
public AbstractVideoResource() {
}
public AbstractVideoResource(String url) {
this.url = url;
}
public void transformMp4() {
System.out.println("视频资源组共有方法-transformMp4()");
}
}
@Data
public abstract class AbstractTextResource implements IResource {
private String url;
public AbstractTextResource() {
}
public AbstractTextResource(String url) {
this.url = url;
}
public void transformUtf8() {
System.out.println("文本资源组共有方法-transformUtf8()");
}
}
@Data
public abstract class AbstractPictureResource implements IResource {
private String url;
public AbstractPictureResource() {
}
public AbstractPictureResource(String url) {
this.url = url;
}
public void transformJpg() {
System.out.println("图片资源组共有方法-transformJpg()");
}
}
public class FileTextResource extends AbstractTextResource {
@Override
public InputStream getInputStream() {
return null;
}
}
public class FileVideResource extends AbstractVideoResource {
@Override
public InputStream getInputStream() {
return null;
}
}
public class FilePictureResource extends AbstractPictureResource {
public FilePictureResource() {
super();
}
public FilePictureResource(String url) {
super(url);
}
@Override
public InputStream getInputStream() {
return null;
}
}
public class HttpPictureResource extends AbstractPictureResource {
public HttpPictureResource() {
super();
}
public HttpPictureResource(String url) {
super(url);
}
@Override
public InputStream getInputStream() {
return null;
}
}
public class HttpTextResource extends AbstractPictureResource {
public HttpTextResource() {
super();
}
public HttpTextResource(String url) {
super(url);
}
@Override
public InputStream getInputStream() {
return null;
}
}
public class HttpVideoResource extends AbstractPictureResource {
public HttpVideoResource() {
super();
}
public HttpVideoResource(String url) {
super(url);
}
@Override
public InputStream getInputStream() {
return null;
}
}
抽象工厂和具体工厂:
public interface IResourceFactory {
public AbstractPictureResource create(String url);
/**
* 加载图片资源的工厂方法
*
* @param url 给的的资源url
* @return 图片资源
*/
public AbstractPictureResource loadPicture(String url);
/**
* 加载视频资源的工厂方法
*
* @param url 给的的资源url
* @return 视频资源
*/
public AbstractVideoResource loadVideo(String url);
/**
* 加载文本资源的工厂方法
*
* @param url 给的的资源url
* @return 文本资源
*/
public AbstractTextResource loadText(String url);
}
public abstract class AbstractResourceFactory implements IResourceFactory {
//可提供共享的模板方法、资源等
}
public class FileResourceFactory extends AbstractResourceFactory {
@Override
public AbstractPictureResource create(String url) {
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
return new FilePictureResource(url);
}
@Override
public AbstractPictureResource loadPicture(String url) {
return null;
}
@Override
public AbstractVideoResource loadVideo(String url) {
return null;
}
@Override
public AbstractTextResource loadText(String url) {
return null;
}
}
public class HttpResourceFactory extends AbstractResourceFactory {
@Override
public AbstractPictureResource create(String url) {
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
//很复杂的逻辑......
return new HttpPictureResource(url);
}
@Override
public AbstractPictureResource loadPicture(String url) {
return null;
}
@Override
public AbstractVideoResource loadVideo(String url) {
return null;
}
@Override
public AbstractTextResource loadText(String url) {
return null;
}
}
测试:
@Slf4j
public class ResourceLoaderMethod {
private IResourceFactory resourceFactory;
private static Map<String, IResourceFactory> resourceFactoryCache = new HashMap<>(8);
static {
//在类加载的时候/起一个定时器,定时读取配置文件
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("resourceFactory.properties");
Properties properties = null;
try {
properties = new Properties();
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
Iterator<Map.Entry<Object, Object>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> next = iterator.next();
String prefixKey = next.getKey().toString();
String className = next.getValue().toString();
Class<?> clazz = null;
try {
clazz = Class.forName(className);
IResourceFactory instance = (IResourceFactory) clazz.getConstructor().newInstance();
resourceFactoryCache.put(prefixKey, instance);
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
private AbstractPictureResource load(String url) {
String prefix = getPrefix(url);
return resourceFactoryCache.get(prefix).create(url);
}
private String getPrefix(String url) {
if (url == null || "".equals(url) || url.contains(":")) {
return "default";
}
String[] split = url.split(":");
return split[0];
}
public static void main(String[] args) {
ResourceLoaderMethod rlm = new ResourceLoaderMethod();
AbstractPictureResource load = rlm.load("file:user/inter");
System.out.println("load.getUrl() = " + load.getUrl());
}
}
这段代码展示了抽象工厂设计模式的实现。
抽象产品和具体产品部分:
IResource
是抽象产品接口,定义了一个 getInputStream()
方法。AbstractVideoResource
、AbstractTextResource
和 AbstractPictureResource
是抽象产品的具体实现,它们分别继承了 IResource
接口,并包含了一些额外的共有方法。FileTextResource
、FileVideoResource
、FilePictureResource
、HttpPictureResource
、HttpTextResource
和 HttpVideoResource
是具体产品类,分别继承了抽象产品的具体实现类,并实现了 getInputStream()
方法。抽象工厂和具体工厂部分:
IResourceFactory
是抽象工厂接口,它定义了创建产品的抽象方法以及加载具体产品的工厂方法。AbstractResourceFactory
是抽象工厂的具体实现,它继承了 IResourceFactory
接口,可以提供共享的模板方法和资源等。FileResourceFactory
和 HttpResourceFactory
是具体工厂类,分别继承了抽象工厂的具体实现类,实现了创建具体产品和加载具体产品的方法。测试部分:
ResourceLoaderMethod
类中的 resourceFactoryCache
是一个静态缓存,用于存储不同前缀对应的具体工厂对象。在类加载时,通过读取配置文件,动态创建具体工厂对象,并将其放入缓存中。load()
方法根据传入的 URL 获取前缀,并从 resourceFactoryCache
缓存中获取对应的具体工厂对象。然后,通过具体工厂对象调用 create()
方法创建抽象产品对象。getPrefix()
方法用于从 URL 中提取前缀,如果 URL 为空、空字符串或包含冒号,则返回默认前缀。在 main()
方法中,创建了 ResourceLoaderMethod
对象,并通过调用 load()
方法加载资源。最后打印出加载的资源的 URL。
这段代码实现了抽象工厂设计模式,通过抽象产品、具体产品、抽象工厂和具体工厂的定义,将产品的创建过程从客户端代码中解耦出来。客户端只需要与抽象工厂进行交互,而不需要直接依赖具体产品类。这提高了代码的灵活性、可扩展性和可维护性。
抽象工厂模式(Abstract Factory Design Pattern)是一种创建型设计模式,它提供一个接口或抽象类作为工厂,允许客户端使用该接口或抽象类来创建一系列相关或依赖的对象。抽象工厂模式旨在提供一种创建对象族的方式,而无需指定具体的类。
抽象工厂模式的优点:
提供了一种统一的接口:抽象工厂模式定义了一组相关对象的创建接口或抽象类,使得客户端可以通过统一的接口来创建一系列相关的产品对象,而无需关心具体的实现类。
实现了产品族的概念:抽象工厂模式支持一次性创建一整个产品族。产品族是指由相互关联或依赖的一组相关对象组成的集合。通过抽象工厂模式,可以一次性创建一整个产品族,确保这些产品对象之间相互匹配、协同工作。
符合开闭原则:抽象工厂模式使得新增产品族变得相对容易。通过添加新的具体工厂类和产品类,可以扩展抽象工厂模式,而无需修改现有的代码,符合开闭原则。
封装了对象的创建细节:抽象工厂模式将对象的创建过程封装在具体工厂类中,使得客户端无需关心对象的具体创建细节,从而提高了代码的封装性和可维护性。
抽象工厂模式的缺点:
难以支持新种类的产品:当需要添加新种类的产品时,需要修改抽象工厂接口或抽象类,以及所有的具体工厂类,这可能对现有的代码和实现造成一定的影响。
增加了系统的复杂性:引入抽象工厂模式会增加系统中的类和对象的数量,这增加了系统的复杂性和理解的难度。
总结:抽象工厂模式提供了一种创建一系列相关或依赖对象的方式,通过一组接口或抽象类定义了对象的创建过程,实现了产品族的概念。它提供了一种统一的接口,封装了对象的创建细节,并符合开闭原则。然而,抽象工厂模式难以支持新种类的产品,并增加了系统的复杂性。根据具体需求,可以考虑使用抽象工厂模式来创建一系列相关的对象。