博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
工厂设计模式究竟怎么写更优雅?!
阅读量:5330 次
发布时间:2019-06-14

本文共 6724 字,大约阅读时间需要 22 分钟。

闲来无事看了菜鸟教程的设计模式。看到了一个很有趣的讨论,该讨论是关于工厂设计模式的书写形式。下面先看一下给出的基础写法,然后再看一下各位网友的优化。

工厂设计模式初衷:我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。即只需要告诉接口想要获取对象的类型,然后接口就会创建好该类型对应的对象,并返回。

类图如:

 

根据上面的类图,可以给出如下实现:

1.首先创建shape.java接口

public interface Shape {   void draw();}

 2.创建接口的三个实现类:

public class Rectangle implements Shape {    @Override   public void draw() {      System.out.println("Inside Rectangle::draw() method.");   }}

 

public class Square implements Shape {    @Override   public void draw() {      System.out.println("Inside Square::draw() method.");   }}

 

public class Circle implements Shape {    @Override   public void draw() {      System.out.println("Inside Circle::draw() method.");   }}

 3.创建工厂:

public class ShapeFactory {       //使用 getShape 方法获取形状类型的对象   public Shape getShape(String shapeType){      if(shapeType == null){         return null;      }              if(shapeType.equalsIgnoreCase("CIRCLE")){         return new Circle();      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){         return new Rectangle();      } else if(shapeType.equalsIgnoreCase("SQUARE")){         return new Square();      }      return null;   }}

 4.使用该工厂,根据传过来的类型信息获取实体类的对象

public class FactoryPatternDemo {    public static void main(String[] args) {      ShapeFactory shapeFactory = new ShapeFactory();       //获取 Circle 的对象,并调用它的 draw 方法      Shape shape1 = shapeFactory.getShape("CIRCLE");       //调用 Circle 的 draw 方法      shape1.draw();       //获取 Rectangle 的对象,并调用它的 draw 方法      Shape shape2 = shapeFactory.getShape("RECTANGLE");       //调用 Rectangle 的 draw 方法      shape2.draw();       //获取 Square 的对象,并调用它的 draw 方法      Shape shape3 = shapeFactory.getShape("SQUARE");       //调用 Square 的 draw 方法      shape3.draw();   }}

 5.输出结果

Inside Circle::draw() method.Inside Rectangle::draw() method.Inside Square::draw() method.

 工厂模式的优缺点:

优点:一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

 

网友优化:

网友A给出的优化方案:

  使用反射机制可以解决每次增加一个产品时,都需要增加一个对象实现工厂的缺点。

  

public class ShapeFactory {    public static Object getClass(Class
clazz) { Object obj = null; try { obj = Class.forName(clazz.getName()).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return obj; }}

 生产对象的时候使用强制转换

Rectangle rect = (Rectangle) ShapeFactory.getClass(Rectangle.class);rect.draw();Square square = (Square) ShapeFactory.getClass(Square.class);square.draw();

 这样只需要一个对象实现工厂,不需要每次增加类型时都需要重新写一个工厂的实现。

网友B对A又进行了进一步优化:

 

public class ShapeFactory {    public static 
T getClass(Class
clazz) { T obj = null; try { obj = (T) Class.forName(clazz.getName()).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return obj; }}

 使用泛型,去掉了每次获取对象时的强制转换

Rectangle rect = ShapeFactory.getClass(Rectangle.class);rect.draw();Shape square = ShapeFactory.getClass(Square.class);square.draw();

 网友C又在B的基础上进一步优化:

针对多个接口实现一个公共的工厂类

public class ObjectFactory {    public 
Object getObject(Class
clazz) { if (clazz == null ) { return null; } Object obj = null; try { obj = Class.forName(clazz.getName()).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { e.printStackTrace(); } return obj; }}

 网友D又在C的基础上进行了一次优化:

public class ShapeFactory {       //使用 getShape 方法获取形状类型的对象  public Shape getShape(Class
clazz){ try { return (IShape) clazz.getConstructor().newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } return null; }}

 接下来有位E网友对以上ABCD网友的写法给出了自己的见解:

  

  其实使用反射是一种不错的办法,但反射也是从类名反射而不能从类反射!

  先看一下工厂模式是用来干什么的——属于创建模式,解决子类创建问题的。换句话来说,调用者并不知道运行时真正的类名,只知道从“Circle"可以创建出一个shape接口的类,至于类的名称是否叫'Circle",调用者并不知情。所以真正的对工厂进行扩展的方式(防止程序员调用出错)可以考虑使用一个枚举类(防止传入参数时,把circle拼写错误)。

   如果调用者参肯定类型是Circle的话,那么其工厂没有存在的意义了!

  比如 IShape shape = new Circle();这样不是更好?也就是说调用者有了Circle这个知识是可以直接调用的,根据DP(迪米特法则)其实调用者并不知道有一个Circle类的存在,他只需要知道这个IShape接口可以计算圆面积,而不需要知道;圆这个类到底是什么类名——他只知道给定一个”circle"字符串的参数,IShape接口可以自动计算圆的面积就可以了!

   其实在.net类库中存在这个模式的的一个典型的。但他引入的另一个概念“可插入编程协议”。

那个就是WebRequest req = WebRequest.Create("");可以自动创建一个HttpWebRequest的对象,当然,如果你给定的是一个ftp地址,他会自动创建一个FtpWebRequest对象。工厂模式中着重介绍的是这种通过某个特定的参数,让你一个接口去干对应不同的事而已!而不是调用者知道了类!

  比如如果圆的那个类名叫"CircleShape“呢?不管是反射还是泛型都干扰了你们具体类的生成!其实这个要说明的问题就是这个,调用者(clinet)只知道IShape的存在,在创建时给IShape一个参数"Circle",它可以计算圆的面积之类的工作,但是为什么会执行这些工作,根据迪米特法则,client是不用知道的。

   我想问一下那些写笔记的哥们,如果你们知道了泛型,那么为什么不直接使用呢?干吗还需要经过工厂这个类呢?不觉得多余了吗?

   如果,我只是说如果,如果所有从IShape继承的类都是Internal类型的呢?而client肯定不会与IShape一个空间!这时,你会了现你根本无法拿到这个类名!

  Create时使用注册机制是一种简单的办法,比如使用一个枚举类,把功能总结到一处。而反射也是一种最简单的办法,调用者输入的名称恰是类名称或某种规则时使用,比如调用者输入的是Circle,而类恰是CircleShape,那么可以通过输入+”Shape"字符串形成新的类名,然后从字符串将运行类反射出来!

   工厂的创建行为,就这些作用,还被你们用反射或泛型转嫁给了调用者(clinet),那么,这种情况下,要工厂类何用?!

 

自认为E的见解是从工厂设计模式的根本出发的,大致可以总结出如下实现:

public enum Factory {    CIRCLE(new Circle(),"CIRCLE"),    RECTANGLE(new Rectangle(),"RECTANGLE"),    SQUARE(new Square(),"SQUARE");        // 成员变量      private Shape shape;      private String name;          // 普通方法      public static Shape getShape(String name) {          for (Factory c : Factory.values()) {              if (c.name == name) {                  return c.shape;              }          }          return null;      }     // 构造方法      private Factory(Shape shape, String name) {          this.shape = shape;          this.name = name;      }     public String getName() {        return name;    }    public Shape getShape() {        return shape;    }    public void setShape(Shape shape) {        this.shape = shape;    }    public void setName(String name) {        this.name = name;    }     }

 可使用枚举根据类型来创建想要的对象:

Factory.getShape("CIRCLE").draw();Factory.getShape("RECTANGLE").draw();Factory.getShape("SQUARE").draw();

 

转载于:https://www.cnblogs.com/blog411032/p/9721599.html

你可能感兴趣的文章
线段树【注意事项】
查看>>
Leetcode 134 Gas Station
查看>>
iptables
查看>>
TF-IDF与余弦相似性的应用(一):自动提取关键词
查看>>
“随时、随性、随遇、随缘、随喜”
查看>>
linux下apache和tomcat整合
查看>>
【结构型模式】《大话设计模式》——读后感 (9)牛市股票还会亏钱?——外观模式...
查看>>
DNN-HMM
查看>>
MQ java 基础编程(一)
查看>>
Asp.net中IsPostBack的实现原理
查看>>
微信小程序 - 配置普通二维码跳小程序
查看>>
es5~es6
查看>>
mongoDB - 安装
查看>>
Python学习1,Python的六大数据类型
查看>>
JavaEE
查看>>
学习MFC创建界面
查看>>
Building QGIS from source - step by step (开发文档翻译1)
查看>>
(转)AIX ODM 简介
查看>>
poj 1191 棋盘分割 (dp)
查看>>
HDU 4069 Squiggly Sudoku(DLX)(The 36th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest)...
查看>>