工厂设计模式究竟怎么写更优雅?!

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

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

类图如:

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

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<?extends Shape> 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> T getClass(Class<? extends T> 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 <T> Object getObject(Class<T> 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("http://ccc......");可以自动创建一个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

时间: 2024-08-02 16:40:48

工厂设计模式究竟怎么写更优雅?!的相关文章

代码这样写更优雅(Python 版)(转载)

转载:https://mp.weixin.qq.com/s?timestamp=1498528588&src=3&ver=1&signature=DfFeOFPXy44ObCMO3yMBLLgt5iFzbFAuShvog4m*JYf1w-gLIDZd7VoPWJN5f36DbVcaBJi33DkfXw6i-h*F7hVyz-wkID*CWQApnqKNCZU-D1o0fNl7h5DnB0HabcBOJV0c7i0*vmiKEZhGAFowkbtf*lioV3DQxtjLDFQI-L

CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅

首页 登录注册 CSS 黑魔法小技巧,让你少写不必要的JS,代码更优雅 阅读 8113 收藏 927 2017-09-26 原文链接:github.com 腾讯云容器服务CSS,立即免费体验容器集群吧!cloud.tencent.com 之前不久,由于自己平时涉猎还算广泛,总结了一篇博客:这些JavaScript编程黑科技,装逼指南,高逼格代码,让你惊叹不已,没想到受到了大家的欢迎,有人希望能博主还能整理个 CSS 的一些黑魔法小技巧,无奈我 CSS 一直很渣,没什么干货,最近写了一个 Chro

简单工厂设计模式

说起设计模式,不得不从最简单的简单工厂设计模式开始说起. 简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常具有共同的父类.因为简单工厂模式中用于创建的方法是静态的,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,属于类创建型模式. 首先我们先看看下面这个类图: 在这个类图中,我们可以发现,静态工厂一共有三个对象是不可缺少的 1公共父类(Fruit接口) 2具体的产品类(Apple, Banana) 3静态工厂类(Simp

【知识点】如何写出优雅的CSS代码 ?

对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于团队合作和后期的维护:而有的混乱,虽然表达出了最终的效果,然而却晦涩难懂,显然团队成员在读这样的代码时就显得无从下手,更不利于后期的维护了.那么如何写出优雅的代码呢?下面我将以一个很小的项目就以下几个方面简单的表达一下自己的看法,如有不妥,望批评指正. 如何整理一个项目. 如何写出清晰易懂的HTML代码. 如何写出优雅的css代

【原创】怎样才能写出优雅的 Java 代码?这篇文章告诉你答案!

本文已经收录自 JavaGuide (59k+ Star):[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识. 本文比较简短,基本就是推荐一些对于写好代码非常有用的文章或者资源.讲真的,下面推荐的文章或者资源强烈建议阅读 3 遍以上. 团队 阿里巴巴Java开发手册(详尽版) https://github.com/alibaba/p3c/blob/master/阿里巴巴Java开发手册(华山版).pdf Google Java编程风格指南: http://hawste

工厂设计模式灵魂拷问-Java实现

show me the code and take to me,做的出来更要说的明白 GitHub项目JavaHouse同步收录 喜欢就点个赞呗! 你的支持是我分享的动力! 引入 我们经常听到工厂模式,比如说 Spring 用了工厂模式啦,面试也会被问到工厂模式等等,这见工厂模式是日常开发中比较重要的一种设计模式.其实工厂模式也是比较简单的一种设计模式,这年头似乎不了解工厂模式,你都不好意思说你学过 编程一样(手动狗头).那么,允许我灵魂拷问一下,你真的了解工厂模式吗. UML类图 不急,我们先

少年,是时候换种更优雅的方式部署你的php代码了

让我们来回忆下上次你是怎么发布你的代码的: 1. 先把线上的代码用ftp备份下来 2. 上传修改了的文件 3. 测试一下功能是否正常 4. 网站500了,赶紧用备份替换回去 5. 替换错了/替换漏了 6. 一台服务器发布成功 7. 登录每一台执行一遍发布操作 8. 加班搞定 9. 老板发飙 ... 尤其现在的互联网行业,讲究快速迭代,小步快跑.像bug修复或者小功能的修改几乎每天都发版本,大功能的版本迭代每周也差不多会有一次.相信不少同行们像我上面说的这样发布自己的代码吧.或者可能先进一点,直接

如何写出优雅的CSS代码 ?(转)

对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于团队合作和后期的维护:而有的混乱,虽然表达出了最终的效果,然而却晦涩难懂,显然团队成员在读这样的代码时就显得无从下手,更不利于后期的维护了.那么如何写出优雅的代码呢?下面我将以一个很小的项目就以下几个方面简单的表达一下自己的看法,如有不妥,望批评指正. 如何整理一个项目. 如何写出清晰易懂的HTML代码. 如何写出优雅的css代

框架基础:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码

距离上一篇博客书写,又过去了大概几个月了,这段时间暂时离开了这个行业,让大脑休息一下.一个人旅行,一个人休息,正好也去完成一个目标 --- 拥有自己的驾照.当然,也把自己晒的黑漆马虎的.不过这一段时间虽然在技术上没有学太多东西,但是在心态上给了自己一个沉淀的机会,感觉自己变得更加沉稳和成熟,感觉这就是自己需要找到的自己,回归自我.好了,废话不多说了,虽然技术上没有学一些新的东西,但是欠的东西还是要补回来的.正如这篇博客,前端Promise规范的实现与ajax技术的集成,当时github上一个用户