工厂模式是最常用的一类设计模式,我们平时所说的工厂模式一般指工厂方法模式,也是使用频率最高的一种工厂模式。工厂模式可以分为三种:简单工厂模式、工厂方法模式和抽象工厂模式。
小弟 简单工厂模式(不属于GoF的23种设计) |
咱们的工厂方法模式 |
大哥 抽象工厂模式 |
定义一个工厂类,可以根据参数的不同返回不同的类型,被创建的实力通常具有共同父类。
简单工厂的组成角色有:抽象产品角色、具体产品角色和工厂角色。
一个抽象产品角色 produce:
package factory.simple;
/**
* 抽象产品
*/
public abstract class Video {
public abstract void produce();
//也可以写所有产品的公共业务方法
public void publicMethod() {
System.out.println("公共特性方法");
}
}
多个具体实现产品 JavaVideo 和 JavaScriptVideo:
package factory.simple;
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制java课程视频");
}
}
package factory.simple;
public class JavaScriptVideo extends Video {
@Override
public void produce() {
System.out.println("录制JavaScript课程视频");
}
}
一个工厂类 VideoFactory :
package factory.simple;
public class VideoFactory {
/**
* 使用if else来返回对象;java SE 7以后switch可以判断String类型了
* @param type
* @return 返回的是工厂生产的对象
*/
public Video getVideo(String type) {
if("java".equalsIgnoreCase(type)) {
return new JavaVideo();
}else if ("javaScript".equalsIgnoreCase(type)) {
return new JavaScriptVideo();
}
return null;
}
/**
* 反射创建对象
* @param c 加载的字节码文件
* @return 返回的是工厂生产的对象
*/
public Video getVideo(Class c) {
Video v = null;
try {
v = (Video) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return v;
}
}
测试类 Main :
package factory.simple;
import java.util.Calendar;
/**
* 简单工厂模式:
* 抽象Video类接收produce方法;
* 通过名字new对象;
* 通过反射创建对象;
*/
public class Main {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video java = videoFactory.getVideo("java");
java.produce();
Video js = videoFactory.getVideo(JavaScriptVideo.class);
js.produce();
Calendar.getInstance();
}
}
测试结果:
可以看到,简单工厂模式对一个产品进行了丰富的实现,客户调用的时候需要指定产品参数,通过具体的工厂类就可以获取到我们指定的对象。
(1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
工厂方法模式的角色组成有:抽象产品、具体产品、抽象工厂、具体工厂。
抽象产品 Video:
package factory.method;
/**
* 抽象产品
*/
public abstract class Video {
public abstract void produce();
}
一些具体实现类:
package factory.method;
public class JavaScriptVideo extends Video {
@Override
public void produce() {
System.out.println("录制javaScript课程视频");
}
}
package factory.method;
public class JavaScriptVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaScriptVideo();
}
}
抽象工厂类 VideoFactory :
package factory.method;
/**
* 工厂方法模式的工厂也是抽象的
*/
public abstract class VideoFactory {
public abstract Video getVideo();
}
抽象工厂的一些具体实现类:
package factory.method;
public class JavaScriptVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaScriptVideo();
}
}
package factory.method;
public class JavaVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
}
测试方法Main:
package factory.method;
/**
* 工厂方法模式(多态工厂模式)相对于简单工厂模式:
* 工厂变成了抽象工厂,需要具体工厂的继承覆写了,通过具体工厂创建实例;
* 优点:增加Video产品时不需要修改原产品类和工厂类了,只需添加具体产品类和具体工厂类;
* 向客户隐藏了那些具体的方法被实例化;
* 工厂确定创建何种产品对象,客户不关心;
* 加入新产品时无需修改抽象工厂和抽象产品提供的接口(可拓展性)。
* 缺点:添加新产品时需要编写新产品和实现工厂,系统中类的数量将成对增加,增加了系统的复杂度,编译数等额外开销;
* 抽象导致的理解难度和阅读难度;
*/
public class Main {
public static void main(String[] args) throws Exception{
VideoFactory javaVideoFactory = new JavaVideoFactory();
VideoFactory javaScriptVideoFactory = new JavaScriptVideoFactory();
Video javaVideo = javaVideoFactory.getVideo();
Video javaScriptVideo = javaScriptVideoFactory.getVideo();
javaVideo.produce();
javaScriptVideo.produce();
//反射方式加载
String factoryName = "factory.method.JavaVideoFactory";
Class> c = Class.forName(factoryName);
VideoFactory videoFactory = (VideoFactory) Class.forName(factoryName).newInstance();
videoFactory.getVideo().produce();
}
}
看一下测试的结果:
工厂方法模式对简单工厂模式的工厂类也进行了抽象和对应实现,成对地增加了文件数量,换来的好处是,工厂方法模式符合软件设计的开闭原则了,对于新的实现产品,我们创建新的实现产品类和新的工厂实现类,通过工厂实现类生产对象,弥补了简单工厂模式的不足。区别于简单工厂模式,工厂方法模式需要知道的是所对应的工厂,不关心具体生产过程。
(1) 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
(2) 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,无需指定它们具体的类,是一种对象创建模式,每个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品。
工厂方法模式的角色组成有:抽象产品、具体产品、抽象工厂、具体工厂。
抽象产品Video 和 Article :
package factory.abs;
/**
* 抽象Video产品
*/
public abstract class Video{
public abstract void produce();
}
package factory.abs;
/**
* 抽象Article产品
*/
public abstract class Article {
public abstract void produce();
}
一些具体产品:
package factory.abs;
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("编写java课程视频");
}
}
package factory.abs;
public class JavaScriptVideo extends Video {
@Override
public void produce() {
System.out.println("编写JavaScript课程视频");
}
}
package factory.abs;
public class JavaArticle extends Article {
@Override
public void produce() {
System.out.println("编写java课程笔记");
}
}
package factory.abs;
public class JavaScriptArticle extends Article {
@Override
public void produce() {
System.out.println("编写JavaScript课程笔记");
}
}
抽象工厂:
package factory.abs;
/**
* 抽象工厂模式的一个工厂生产一个产品族(有多个方法)
*/
public interface CourseFactory {
Video getVideo();
Article getArticle();
}
package factory.abs;
/*
* Java课程的实现工厂
*/
public class JavaCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
@Override
public Article getArticle() {
return new JavaArticle();
}
}
package factory.abs;
/**
* JavaScript课程的实现工厂
*/
public class JavaScriptCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new JavaScriptVideo();
}
@Override
public Article getArticle() {
return new JavaScriptArticle();
}
}
测试类Main,根据不同的实现工厂,创建不同的产品族:
package factory.abs;
/**
* 抽象工厂模式
* 抽象工厂提供一个产品族,
* 客户端指定具体工厂就可以获取该工厂的产品族
* 通过不同的工厂实现类进行实例化
* 通过反射和配置文件可以实现修改具体工厂时不修改客户端代码,只改配置文件
*
* 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,
* 更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接
* 口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
*
* 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产
* 品族中的对象。
*
* 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
*/
public class Main {
public static void main(String[] args) throws Exception{
JavaCourseFactory javaCourseFactory = new JavaCourseFactory();
Article article = javaCourseFactory.getArticle();
Video video = javaCourseFactory.getVideo();
article.produce();
video.produce();
System.out.println("-------------------------");
JavaScriptCourseFactory javaScriptCourseFactory = new JavaScriptCourseFactory();
Article article1 = javaScriptCourseFactory.getArticle();
Video video1 = javaScriptCourseFactory.getVideo();
article1.produce();
video1.produce();
System.out.println("-------------------------");
String factoryName = "factory.abs.JavaCourseFactory";
refCreateFactory(factoryName);
System.out.println("-------------------------");
String factoryName1 = "factory.abs.JavaScriptCourseFactory";
refCreateFactory(factoryName1);
}
private static void refCreateFactory(String factoryName) throws Exception{
Class> c = Class.forName(factoryName);
CourseFactory courseFactory = (CourseFactory) c.newInstance();
courseFactory.getArticle().produce();
courseFactory.getVideo().produce();
}
}
测试结果:
工厂方法模式对开闭原则进行了扩展,抽象工厂模式对工厂生产的产品进行了升级,现在可以生产产品族了,比如上例中列举的生产Java课程产品族和JavaScript产品族;抽象工厂模式依然增加文件数量来拥抱开闭原则,客户不需要知道什么被创建,抽象工厂中的方法数量增多了。
在以下情况下可以考虑使用抽象工厂模式:
(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。
(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。
(3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系的,此时具有一个共同的约束条件:操作系统的类型。
(4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
参考链接
Java设计模式链接:https://pan.baidu.com/s/1f106MtK6NERPwe62DlWHlA 提取码:jh4r