工厂模式--摆脱你日复一日new对象却依旧单身的苦恼!
前言
每每谈及到Java,就不免会想到一个悲伤的事实:你是否每天都在new对象,却依然坚守在单身岗上屹立不倒。(所谓面向对象编程hhh),这篇来学一下工厂模式,摆脱new对象的苦恼!
知识点
传统工厂
- 抽象类和子类们
- 生产和使用放在一起了没有分离开,使用时传名字,然后来生产相应的产品
public class OrderPizza {
// 构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购披萨的类型
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
//输出pizza 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
改进的思路分析分析
- 修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有多处。
- 思路:把创建Pizza对象封装到一个工厂类中,这样我们有新的Pizza种类时,只需要修改该工厂类就好,其它有创建到Pizza对象的代码就不需要修改了.->简单工厂模式
简单工厂模式(传产品名字然后if-else)
一个超级工厂,负责生产所有种类的产品
- 在传统工厂的基础上,通过将产品创建的过程封装成一个工厂类,实现生产和使用分离开
接口类/抽象类
package com.melo.design.工厂模式.简单工厂模式;
public interface Shape {
void draw();
}
子类产品
package com.melo.design.工厂模式.简单工厂模式;
public class Circle implements Shape{
@Override
public void draw() {
System.out.println("Circle");
}
}
工厂类
package com.melo.design.工厂模式.简单工厂模式;
public class ShapeFactory {
public static Shape getShape(String shape){
if("Circle".equals(shape)){
return new Circle();
}else if("Triangle".equals(shape)){
return new Triangle();
}
return null;
}
}
调用方
package com.melo.design.工厂模式.简单工厂模式;
public class Main {
public static void main(String[] args) {
Shape circle = ShapeFactory.getShape("Circle");
circle.draw();
}
}
缺点
总而言之,简单工厂模式就是让一个工厂类承担构建所有对象的职责。调用者需要什么产品,让工厂生产出来即可。它的弊端也显而易见:
- 一是如果需要生产的产品过多,此模式会导致工厂类过于庞大,承担过多的职责,变成超级类。当苹果生产过程需要修改时,要来修改此工厂。梨子生产过程需要修改时,也要来修改此工厂。也就是说这个类不止一个引起修改的原因。违背了单一职责原则。
- 二是当要生产新的产品时,必须在工厂类中添加新的分支。而开闭原则告诉我们:类应该对修改封闭。我们希望在添加新功能时,只需增加新的类,而不是修改既有的类,所以这就违背了开闭原则。
工厂方法模式
- 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。
- 工厂方法让类的实例化推迟到子类中进行!!!
原本简单工厂是一个工厂,负责生产所有的产品,现在要改成每个对象对应一个工厂,一个工厂只负责生产一个产品
优点
- 这样我们就解决了上文所述的单一职责问题,每个工厂只负责生产一个产品
- 同时也解决了开闭原则,每次需要新增一个产品的时候,我们只需要新增一个工厂类就好,而不需要去修改原来的工厂类
缺点
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
每个产品对应一个工厂类,然后同一类的产品,比如都是水果类,则再抽象一个水果工厂出来,具体的工厂类去实现他的create方法
MyDemo
水果接口
package com.melo.mydesign.Factory.FactoryMethod.Product;
//产品只关心使用,不关心创建
public interface Fruit {
void eat();
}
水果具体子类
package com.melo.mydesign.Factory.FactoryMethod.Product;
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
package com.melo.mydesign.Factory.FactoryMethod.Product;
public class Pear implements Fruit {
@Override
public void eat() {
System.out.println("吃梨子");
}
}
水果工厂接口
package com.melo.mydesign.Factory.FactoryMethod.Factory;
import com.melo.mydesign.Factory.FactoryMethod.Product.Fruit;
public interface FruitFactory {
Fruit createFruit();
}
具体水果工厂实现子类
package com.melo.mydesign.Factory.FactoryMethod.Factory;
import com.melo.mydesign.Factory.FactoryMethod.Product.Apple;
import com.melo.mydesign.Factory.FactoryMethod.Product.Fruit;
public class AppleFactory implements FruitFactory {
@Override
public Fruit createFruit() {
return new Apple();
}
}
package com.melo.mydesign.Factory.FactoryMethod.Factory;
import com.melo.mydesign.Factory.FactoryMethod.Product.Fruit;
import com.melo.mydesign.Factory.FactoryMethod.Product.Pear;
public class PearFactory implements FruitFactory {
@Override
public Fruit createFruit() {
return new Pear();
}
}
具体使用
package com.melo.mydesign.Factory.FactoryMethod;
import com.melo.mydesign.Factory.FactoryMethod.Factory.AppleFactory;
import com.melo.mydesign.Factory.FactoryMethod.Factory.FruitFactory;
import com.melo.mydesign.Factory.FactoryMethod.Product.Fruit;
public class Consumer {
public static void main(String[] args) {
//使用者不需要知道是哪个类在生产,面对的都是 FruitFactory 这个接口
FruitFactory appleFactory = new AppleFactory();
Fruit apple = appleFactory.createFruit();
apple.eat();
}
}
抽象工厂模式
- 工厂方法模式,随着产品越来越多,会产生越来越多的产品类和产品工厂,能不能把一类产品,抽离出来变成一个产品家族呢?
这意味着不再是每一个产品就对应一个产品工厂了
而是一类产品,对应一个产品类工厂
以菜鸟的为例子(感觉比较权威一点)
UML
- 首先从使用者的角度,要生产一个产品,一开始肯定是先找到那个产品所在的产品类工厂
如何找到产品类工厂?相当于产品类工厂也需要有人来创造他,又是处理多个产品的工厂类,我们叫他FactoryProducer
- 然后拿到具体的产品类工厂了,就要生产具体的产品,处理方式同上(if-else)
- 剩下的就是产品和父类产品了,具体实现代码见下方
可以看到Shape工厂要负责多个Shape了,就回归到简单工厂,一个工厂处理多个产品(需要传字符串名字然后if-else)
抽象工厂和具体产品类工厂(注意需要把工厂类的方法都定义出来)
具体实现看子类
- 可以看到Shape产品类工厂,也得实现Color的方法,只不过return null罢了
同时,FactoryProducer也得处理多个,既要处理Shape也要处理Color
具体使用
MyDemo
抽象产品类工厂
package com.melo.mydesign.Factory.AbstractFactory.Factory;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.Car;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Fruit;
public interface AbstractFactory {
Fruit createFruit(String fruitName);
Car createCar(String carName);
}
具体产品类工厂--Car
package com.melo.mydesign.Factory.AbstractFactory.Factory;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.Audi;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.BMW;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.Car;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Fruit;
public class CarFactory implements AbstractFactory{
@Override
public Fruit createFruit(String fruitName) {
return null;
}
public Car createCar(String carName){
if(carName.equalsIgnoreCase("BMW")){
return new BMW();
}else if(carName.equalsIgnoreCase("Audi")) {
return new Audi();
}
return null;
}
}
具体产品类工厂--Fruit
package com.melo.mydesign.Factory.AbstractFactory.Factory;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.Car;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Pear;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Apple;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Fruit;
public class FruitFactory implements AbstractFactory {
public Fruit createFruit(String fruitName){
if(fruitName.equalsIgnoreCase("Apple")){
return new Apple();
}else if(fruitName.equalsIgnoreCase("Pear")) {
return new Pear();
}
return null;
}
@Override
public Car createCar(String carName) {
return null;
}
}
FactoryProducer--负责生产产品类工厂的对象
package com.melo.mydesign.Factory.AbstractFactory.Factory;
public class FactoryProducer {
public static AbstractFactory produceFactory(String factoryName){
if(factoryName.equalsIgnoreCase("Fruit")){
return new FruitFactory();
}else if (factoryName.equalsIgnoreCase("Car")){
return new CarFactory();
}
return null;
}
}
Car类产品接口
package com.melo.mydesign.Factory.AbstractFactory.Product.Car;
//产品只关心使用,不关心创建
public interface Car {
void eat();
}
Car类产品实现类
package com.melo.mydesign.Factory.AbstractFactory.Product.Car;
public class BMW implements Car {
@Override
public void eat() {
System.out.println("宝马");
}
}
Fruit类产品
package com.melo.mydesign.Factory.AbstractFactory.Product.Fruit;
//产品只关心使用,不关心创建
public interface Fruit {
void eat();
}
Fruit类实现类
package com.melo.mydesign.Factory.AbstractFactory.Product.Fruit;
import com.melo.mydesign.Factory.AbstractFactory.Product.Fruit.Fruit;
public class Pear implements Fruit {
@Override
public void eat() {
System.out.println("吃梨子");
}
}
消费者
package com.melo.mydesign.Factory.AbstractFactory;
import com.melo.mydesign.Factory.AbstractFactory.Factory.AbstractFactory;
import com.melo.mydesign.Factory.AbstractFactory.Factory.FactoryProducer;
import com.melo.mydesign.Factory.AbstractFactory.Product.Car.Car;
public class Consumer {
public static void main(String[] args) {
AbstractFactory carFactory = FactoryProducer.produceFactory("Car");
Car bmw = null;
if (carFactory != null) {
bmw = carFactory.createCar("BMW");
}
if (bmw != null) {
bmw.eat();
}
}
}
缺点
- 不再符合开闭原则了,因为本质是由简单工厂去升级而来的,又回归到了一个产品类工厂,需要处理多个产品的问题,**就需要if-else了 **
总结
- 一个小小的工厂模式,就把前边学到的几大原则: 开闭原则 ,依赖倒转原则(善用抽象接口而不是具体实现) 体现得淋漓尽致.
- 产品一旦多起来,就要抽离出一个抽象的产品类接口,让子类去实现他,使用者只关心抽象,而不关心具体的实现,不需要知道具体的类名,想生成苹果就直接 Fruit apple = new Apple(); 而不用 Apple apple = new Apple();
- 而如果生产的过程变得复杂起来,比如苹果还需要各种处理,就需要将创建的过程封装起来,不暴露给使用者,这时就需要用到专门的工厂类来进行包装了,外界只需要从工厂里边拿就好了,传一个简单的"苹果"字符串,这就演变到了简单工厂模式了
- 再然后我们发现一个简单工厂类,名曰简单,但是却要处理各种各样的产品,每次新增一个产品,都得去修改if-else代码块,不符合开闭原则
- 由此引入了 工厂方法模式 让每一个产品就去对应有一个产品工厂,专门的工厂来生产产品,一个工厂只负责一个产品的生成,满足了单一职责原则,同时每次新增产品,只需要多加一个产品工厂就好了,无需修改原来的工厂,又解决了开闭原则的问题
- 然而,随着产品数量越来越大,单纯一个工厂负责一个产品的话,工厂的数量也许会越来越多,而且,这些产品之前可能存在某些联系,比如都是水果类,那我们何不抽离出一个水果工厂,来生产各式各样的水果呢
看到这里可能有细心的读者就发现了,一个工厂又要去负责生产多个产品了,那似乎又回到了简单工厂模式,不符合开闭原则和单一职责了,是的,抽象工厂模式其实就是简单工厂的升级版而已!
参考
如何学习设计模式? - 力扣(LeetCode)的回答 - 知乎 https://www.zhihu.com/question/308850392/answer/1324509357
工厂模式--摆脱你日复一日new对象却依旧单身的苦恼!的更多相关文章
- JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象
本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...
- .NET设计模式: 工厂模式
.NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html .NET设计模式(1): ...
- 简单工厂模式(Simple Factory Pattern)
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一.简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例.简单工厂 ...
- 设计模式之简单工厂模式(Simply Factory)摘录
从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫静态工厂方法(Static Factory Method)模式.但不属于23种GOF设计模式之中的一个.简单工厂模式是由一个工厂对象决定创建出 ...
- 从接口、抽象类到工厂模式再到JVM来总结一些问题
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习! 涉及到的知识点总结如下: 为什么使用接口? 接口和抽象类的区别 简单工厂模式总结 Java中new和newInstance的区别 J ...
- Java设计模式(一) 简单工厂模式不简单
摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...
- Head First 设计模式之工厂模式(Factory Pattern)
前言: 除了使用new操作符之外,还有更多制造对象的方法.你将了解到实例化这个活动不应该总是公开的进行,也会意识到初始化会造成“耦合”的问题.工厂模式将会从复杂的依赖中帮你脱困. 1. 简单的工厂 ...
- [python实现设计模式]-3.简单工厂模式-触宝开放平台
预备知识: 开放封闭原则(Open-Closed Principle OCP) Software entities(classes,modules,functions etc) should open ...
- GOF业务场景的设计模式-----工厂模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类. 工厂方法模式 基本代码 interface IProduct { public void produ ...
随机推荐
- 使用kubeadm安装kubernetes 1.21
文章原文 配置要求 至少2台 2核4G 的服务器 本文档中,CPU必须为 x86架构 CentOS 7.8 或 CentOS Stream 8 安装后的软件版本为 Kubernetes v1.21.x ...
- vim编辑器设置
由于ubantu自带的vi编辑器并不好用,而开发一般使用vim编辑器,vim需要自己安装(sudo apt-get install vim 即可安装),但是默认的设置使用起来很不舒服,因此可以通过修改 ...
- 两种方式配置vue全局方法
目录 1,前言 2,第一种方式 3,第二种方式 1,前言 在Vue项目开发中,肯定会有这样一个场景:在不同的组件页面用到同样的方法,比如格式化时间,文件下载,对象深拷贝,返回数据类型,复制文本等等.这 ...
- CSS001. 纯CSS实现瀑布流(纵向排序)
通过 Multi-columns 相关的属性 column-count.column-gap 配合 break-inside 来实现瀑布流布局. 首先对包裹图片的盒子增加样式,column-count ...
- 判断input radio选中那个
var _sex=$("input[name='sex']:checked").val(); if(_sex==null){ layer.msg("请选择性别" ...
- Qt+Python开发百度图片下载器
一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->收费->百度图片下载器(可试用5分钟) 安装包直接下载地址:htt ...
- oracle报错注入的一些函数
oracle 报错注入 select dbms_xmltranslations.extractxliff((select banner from sys.v_$version where rownum ...
- Request 根据用户输入的信息获取输入到控制台
html代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- 技术栈:springboot2.x,vue,activiti5.22,mysql,带工作流系统
前言 activiti工作流,企业erp.oa.hr.crm等审批系统轻松落地,请假审批demo从流程绘制到审批结束实例. 一.项目形式 springboot+vue+activiti集成了activ ...
- PHP中的MySQLi扩展学习(三)mysqli的基本操作
我们继续 MySQLi 扩展的学习,上篇文章中提到过,MySQLi 的扩展相对于 PDO 来说功能更加的丰富,所以我们依然还会在学习过程中穿插各种 MySQLi 中好玩的方法函数.不过,今天的主角是 ...