Java设计模式之工厂模式
工厂模式是一种非常常用的创建型设计模式,提供了创建对象的最佳方式。在创建对象时,不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。今天我们就一起学习一下工厂模式。
工厂模式可以分为3类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
1、简单工厂模式
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。
定义:定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
适用场景:其实由定义也大概能推测出其使用场景,首先由于只有一个工厂类,所以工厂类中创建的对象不能太多,否则工厂类的业务逻辑就太复杂了,其次由于工厂类封装了对象的创建过程,所以客户端应该不关心对象的创建。总结一下适用场景:
(1)需要创建的对象较少。
(2)客户端不关心对象的创建过程。
以上就是简单工厂模式简单工厂模式的适用场景,下面看一个具体的实例。
实例:创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,不看代码先考虑一下如何通过该模式设计完成此功能。
圆形,正方形,三角形都属于一种图形,并且都具有draw方法,所以首先可以定义一个接口或者抽象类,作为这三个图像的公共父类,并在其中声明一个公共的draw方法,然后每个图形各自去实现这个公共接口中的方法。
定义一个共同的接口
这里定义成抽象类也是可以的,只不过接口是更高一级的抽象,所以习惯定义成接口,而且接口支持多实现,方便以后扩展。
public interface Shape {
void draw();
}
接口的实现类
public class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("长方形...");
}
}
public class Round implements Shape{
@Override
public void draw() {
System.out.println("圆形...");
}
}
public class Square implements Shape{
@Override
public void draw() {
System.out.println("正方形...");
}
}
工厂类的具体实现
public class ShapeFactory {
//根据给定的形状名称,创建不同的形状实例。
public static Shape getShape(String typeName){
Shape shape = null;
if ("round".equalsIgnoreCase(typeName)){
shape = new Round();
}
if ("rectangle".equalsIgnoreCase(typeName)){
shape = new Rectangle();
}
if ("square".equalsIgnoreCase(typeName)){
shape = new Square();
}
return shape;
}
}
在这个工厂类中通过传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
客户端使用
public class Test {
public static void main(String[] args) {
Shape round = ShapeFactory.getShape("round");
round.draw();
Shape rectangle = ShapeFactory.getShape("rectangle");
rectangle.draw();
Shape square = ShapeFactory.getShape("square");
square.draw();
}
}
//结果
这是一个圆形.......
这是一个长方形......
这是一个正方形....
只通过给ShapeFactory传入不同的参数就实现了各种形状的绘制,这个就是简单工厂模式。
但是这样还是有一点问题的,因为输入参数时用户可能会写错,如果写错了,那么工厂就不能返回正确的示例对象了,为了解决这个问题,我们可以用枚举的方式进行一些简单的优化。
定义一个枚举类
public enum ShapeType {
ROUND,
RECTANGLE,
SQUARE;
}
修改一下工厂中的方法
public class ShapeFactory {
//方法的参数改为我们定义的枚举类型
public static Shape getShape(ShapeType shapeType){
Shape shape = null;
if ("round".equalsIgnoreCase(shapeType.name())){
shape = new Round();
}
if ("rectangle".equalsIgnoreCase(shapeType.name())){
shape = new Rectangle();
}
if ("square".equalsIgnoreCase(shapeType.name())){
shape = new Square();
}
return shape;
}
}
这个时候客户端就要这样用了
public class Test {
public static void main(String[] args) {
//我们只需要传入相应的枚举类型即可,不用再去手动拼写了,有效防止因为拼写错误而导致程序运行出错。
Shape round = ShapeFactory.getShape(ShapeType.ROUND);
round.draw();
Shape rectangle = ShapeFactory.getShape(ShapeType.RECTANGLE);
rectangle.draw();
Shape square = ShapeFactory.getShape(ShapeType.SQUARE);
square.draw();
}
}
2、工厂方法模式
简单工厂模式违背了开闭原则,而工厂方法模式则是简单工厂模式的进一步深化,其不像简单工厂模式通过一个工厂来完成所有对象的创建,而是通过不同的工厂来创建不同的对象,每个对象有对应的工厂创建。
定义:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。这次我们先用实例详细解释一下这个定义,最后在总结它的使用场景。
实例:这一次我们需要设计一个图片加载类,它具有多个图片加载器,用来加载jpg,png,gif格式的图片,每个加载器都有一个read()方法,用于读取图片。下面我们完成这个图片加载类。
定义一个公共接口,图片加载器接口
public interface Reader {
void read();
}
具体实现类
public class JPGReader implements Reader{
@Override
public void read() {
System.out.println("read jpg...");
}
}
public class PngReader implements Reader{
@Override
public void read() {
System.out.println("read png...");
}
}
public class GIFReader implements Reader{
@Override
public void read() {
System.out.println("read gif...");
}
}
现在我们按照定义所说定义一个抽象的工厂接口ReaderFactory
public interface ReaderFactory {
public Reader getReader();
}
里面有一个getReader()方法返回我们的Reader 类,接下来我们把上面定义好的每个图片加载器都提供一个工厂类,这些工厂类实现了ReaderFactory 。
JPG加载器工厂
public class JPGFactory implements ReaderFactory{
@Override
public Reader getReader() {
return new JPGReader();
}
}
PNG加载器工厂
public class PNGFactory implements ReaderFactory{
@Override
public Reader getReader() {
return new PngReader();
}
}
GIF加载工厂
public class GIFFactory implements ReaderFactory{
@Override
public Reader getReader() {
return new GIFReader();
}
}
在每个工厂类中我们都通过复写的getReader()方法返回各自的图片加载器对象。
客户端使用
public class Test {
public static void main(String[] args) {
//读取JPG
ReaderFactory jpgFactory = new JPGFactory();
Reader jpgReader = jpgFactory.getReader();
jpgReader.read();
//读取PNG
ReaderFactory pngFactory = new PNGFactory();
Reader pngReader = pngFactory.getReader();
pngReader.read();
//读取GIF
ReaderFactory gifFactory = new GIFFactory();
Reader gifReader = gifFactory.getReader();
gifReader.read();
}
}
可以看到上面三段代码,分别读取了不同格式的图片,不同之处在于针对不同的图片格式声明了不同的工厂,进而创建了相应的图片加载器。
和简单工厂对比一下,最根本的区别在于简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类,这些工厂类都实现了一个工厂基类(本例中的ReaderFactory )。下面总结一下工厂方法的适用场景。
适用场景:
(1)客户端不需要知道它所创建的对象的类。例子中我们不知道每个图片加载器具体叫什么名,只知道创建它的工厂名就完成了创建过程。
(2)客户端可以通过子类来指定创建对应的对象。 以上场景使用于采用工厂方法模式。
3、抽象工厂模式
抽象工厂模式是工厂方法的进一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。
定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。( 在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的对象)
图片源于网络,侵删
抽象工厂和工厂方法一样可以划分为4大部分:
- AbstractFactory(抽象工厂)工厂方法模式的核心,与应用程序无关。任何在模式中创建对象的工厂类必须实现这个接口。
- ConcreteFactory(具体工厂):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用就可以创建某一种产品对象。
- AbstractProduct(抽象产品):工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
- ConcreteProduct(具体产品):抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。在抽象工厂中创建的产品属于同一产品族,这不同于工厂模式中的工厂只创建单一产品。
下面还是先看一个具体实例。
实例:现在需要做一款跨平台的游戏,需要兼容Android,IOS,Windows PC三个移动操作系统,该游戏针对每个系统都设计了一套操作控制器(OperationController)和界面控制器(UIController),下面通过抽象工厂方式完成这款游戏的架构设计。
定义公共接口OperationController 和 UIController --> 抽象产品
public interface OperationController {
void control();
}
public interface UIController {
void display();
}
完成各个操作系统的不同实现 --> 具体产品
Android
public class AndroidOperationController implements OperationController {
@Override
public void control() {
System.out.println("Android OperationController System");
}
}
public class AndroidUIController implements UIController {
@Override
public void display() {
System.out.println("Android UIController System");
}
}
IOS
public class IOSOperationController implements OperationController {
@Override
public void control() {
System.out.println("IOS OperationController System");
}
}
public class IOSUIController implements UIController {
@Override
public void display() {
System.out.println("IOS UIController System");
}
}
WP
public class WpOperationController implements OperationController {
@Override
public void control() {
System.out.println("Wp OperationController System");
}
}
public class WpUIController implements UIController {
@Override
public void display() {
System.out.println("Wp UIController System");
}
}
然后我们在定义一个抽象工厂类,这个工厂可以生产OperationController 和 UIController --> 抽象工厂
public interface SystemFactory {
public OperationController createOperationController();
public UIController createUIController();
}
接着我们再来写各个平台的具体工厂实现 --> 具体工厂
Android
public class AndroidFactory implements SystemFactory{
@Override
public OperationController createOperationController() {
return new AndroidOperationController();
}
@Override
public UIController createUIController() {
return new AndroidUIController();
}
}
IOS
public class IOSFactory implements SystemFactory{
@Override
public OperationController createOperationController() {
return new IOSOperationController();
}
@Override
public UIController createUIController() {
return new IOSUIController();
}
}
WP
public class WPFactory implements SystemFactory{
@Override
public OperationController createOperationController() {
return new WpOperationController();
}
@Override
public UIController createUIController() {
return new WpUIController();
}
}
使用示例:
public class Test {
public static void main(String[] args) {
//android操作系统使用示例
SystemFactory androidFactory = new AndroidFactory();
OperationController operationController = androidFactory.createOperationController();
UIController uiController = androidFactory.createUIController();
operationController.control();
uiController.display();
//其他操作系统使用方式一样
}
}
学习设计模式的过程是潜移默化的,要想真正领悟其中的思想,需要多思考并结合大量的实践,所以学习过程中不要着急,一定要耐心学习,并在实际场景中多去思考是否可以应用所学的设计模式来编写代码,这样才能慢慢提升我们的编码质量。