热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

别再到处new对象了,试试3大工厂模式,真香!!

你还在到处new对象吗?单身狗:我没对象,new怎么了?new对象本身是没问题的,但也不能全部new关键字走天下,其实有更好的方式,合适的时候可以试试工厂模式,代

别再到处 new 对象了,试试 3 大工厂模式,真香!!

你还在到处 new 对象吗?

单身狗:我没对象,new 怎么了?

new 对象本身是没问题的,但也不能全部 new 关键字走天下,其实有更好的方式,合适的时候可以试试工厂模式,代码会更优雅。

什么是工厂模式?

顾名思义,工厂模式中的 "工厂" 指的是创建对象的工厂,它提供了一种创建对象的最佳方式,也就是工厂模式。

工厂模式的好处是这些对象不需要暴露自身的创建过程,统一由工厂模式进行创建和提供,隐藏了创建细节,避免了错误的创建对象的形式,也减少了重复创建冗余代码。

一般情况下,工厂模式可以细分为三类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

不过在设计模式权威书籍《设计模式:可复用面向对象软件的基础》一书中,简单工厂模式只是工厂方法模式的一个特例而已。

所以,从权威的角度说,工厂模式只分为: 工厂模式抽象工厂模式 两大类。

但不管白猫黑猫,能抓老鼠的就是好猫,设计模式亦是如此,不管怎么分类,这些模式都是程序员们历年过往经验的浓缩,都是值得学习和借鉴的。

所以,本文栈长从细分的角度带大家来实战下这三个工厂设计模式。

1、简单工厂

比如 XX 公司是做支付的,公司有几大类的客户:电商商户、银行客户、代理商……

创建这些客户的时候我们可以用简单工厂模式来实现看看。

新建客户基类:

可以把所有客户公共的信息放到一个客户基类中,比如:客户名、客户类型等,所有的客户继承这个抽象基类。

/**
 * 客户
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class Customer {

    /**
     * 客户名称
     */
    private String name;

    /**
     * 客户类型
     */
    private String type;

}

新建电商商户类:

/**
 * 商户
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class Merchant extends Customer {

    /**
     * 合同类型
     */
    private int contractType;

    /**
     * 结算周期(天)
     */
    private int settmentDays;

    public Merchant(String name, String type) {
        super(name, type);
    }
}

新建银行客户类:

/**
 * 银行客户
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class BankPartner extends Customer {

    /**
     * 银行编码
     */
    private String code;

    /**
     * 银行地址
     */
    private String address;

    public BankPartner(String name, String type) {
        super(name, type);
    }
}

新建代理商类:

/**
 * 代理商
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class Agent extends Customer {

    /**
     * 代理周期
     */
    private int period;

    /**
     * 代理产品
     */
    private int[] products;

    public Agent(String name, String type) {
        super(name, type);
    }
}

新增简单工厂类:

新建一个简单工厂,提供一个公共静态方法,根据不同的客户类型创建不同的客户。

/**
 * 客户简单工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class CustomerFactory {

    private static Merchant createMerchant(String type, String name) {
        return new Merchant(type, name);
    }

    private static BankPartner createBankPartner(String type, String name) {
        return new BankPartner(type, name);
    }

    private static Agent createAgent(String type, String name) {
        return new Agent(type, name);
    }

    public static Customer create(String type, String name) {
        if ("M".equals(type)) {
            return createMerchant(type, name);
        } else if ("B".equals(type)) {
            return createBankPartner(type, name);
        } else if ("A".equals(type)) {
            return createAgent(type, name);
        }
        return null;
    }

}

新建测试类:

/**
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class Test {

    public static void main(String[] args) {
        Customer merchant = CustomerFactory.create("M", "Java技术栈商户");
        System.out.println(merchant);

        Customer bankPartner = CustomerFactory.create("B", "Java技术栈银行客户");
        System.out.println(bankPartner);

        Customer agent = CustomerFactory.create("A", "Java技术栈代理商");
        System.out.println(agent);
    }

}

输出结果:

本节教程所有实战源码已上传到这个仓库:

https://github.com/javastacks/javastack

可以看出简单工厂的使用很简单,就是耦合性太高了。

第一,对象和基类之间是基于继承的。

第二,工厂类耦合了不同对象的创建,如果对象类型不是固定或者经常变动的,就要频繁修改工厂类,比如我现在要再加一种客户,就必须要改动工厂类,不符开闭原则。

所以,简单工厂只适用于固定类型对象的创建。

2、工厂方法

工厂方法就是为某类产品提供一个工厂接口,然后为每个产品提供一个工厂实现类。

废话少说,我们将简单工厂的示例用工厂方法再改造一下。

新建工厂方法接口:

/**
 * 工厂方法客户接口
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public interface CustomerFactory {

    Customer create(String type, String name);

}

新建商户工厂实现类:

/**
 * 商户工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Merchant(type, name);
    }

}

新建银行客户工厂实现类:

/**
 * 银行客户工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new BankPartner(type, name);
    }

}

新建代理商工厂实现类:

/**
 * 代理商工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Agent(type, name);
    }

}

新建测试类:

/**
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------工厂模式-工厂方法------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.create("M", "Java技术栈商户");
        System.out.println(merchant);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.create("B", "Java技术栈银行客户");
        System.out.println(bankPartner);

        CustomerFactory agentFactory  = new AgentFactory();
        Customer agent = agentFactory.create("A", "Java技术栈代理商");
        System.out.println(agent);
    }

}

输出结果:

本节教程所有实战源码已上传到这个仓库:

https://github.com/javastacks/javastack

可以看出,工厂方法也是挺简单易用的,耦合性问题也解决了,每增加一个产品就新增一个产品工厂实现类就行了,扩展性非常好。

但也有一个问题,如果产品非常多,那势必会造成工厂实现类泛滥,另外一种可怕的场景就是,如果涉及到工厂接口变更,工厂实现类的维护简直就是一种恶梦。

3、抽象工厂

工厂方法中一个工厂只能创建一个对象,如果现在每次创建客户的时候都需要同时创建一份客户扩展资料,那就可以考虑使用抽象工厂。

新建客户扩展基类:

可以把所有客户公共的扩展信息放到一个客户扩展基类中,比如:客户曾用名、客户扩展说明等,所有的客户继承这个扩展抽象基类。

/**
 * 客户扩展
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@NoArgsConstructor
public abstract class CustomerExt {

    /**
     * 客户曾用名
     */
    private String formerName;

    /**
     * 客户扩展说明
     */
    private String note;

}

新建商户扩展类:

/**
 * 商户
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class MerchantExt extends CustomerExt {

    /**
     * 介绍人
     */
    private int introduceName;

    /**
     * 介绍人电话
     */
    private String introduceTel;

}

新建银行客户扩展类:

/**
 * 银行客户扩展
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class BankPartnerExt extends CustomerExt {

    /**
     * 分行个数
     */
    private int branchCount;

    /**
     * ATM个数
     */
    private int atmCount;

}

新建代理商扩展类:

/**
 * 商户
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
@Data
@ToString(callSuper = true)
public class AgentExt extends CustomerExt {

    /**
     * 来源
     */
    private String source;

    /**
     * 资质
     */
    private String certification;

}

新建抽象工厂接口:

/**
 * 抽象工厂客户接口
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public interface CustomerFactory {

    Customer createCustomer(String type, String name);

    CustomerExt createCustomerExt();

}

新建商户工厂实现类:

/**
 * 商户工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Merchant(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new MerchantExt();
    }

}

新建银行客户工厂实现类:

/**
 * 银行客户工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new BankPartner(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new BankPartnerExt();
    }

}

新建代理商工厂实现类:

/**
 * 代理商工厂
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Agent(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new AgentExt();
    }

}

新建测试类:

/**
 * @author: 栈长
 * @from: 公众号Java技术栈
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------工厂模式-抽象工厂------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.createCustomer("M", "Java技术栈商户");
        CustomerExt merchantExt = merchantFactory.createCustomerExt();
        System.out.println(merchant);
        System.out.println(merchantExt);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.createCustomer("B", "Java技术栈银行客户");
        CustomerExt bankPartnerExt = bankPartnerFactory.createCustomerExt();
        System.out.println(bankPartner);
        System.out.println(bankPartnerExt);

        CustomerFactory agentFactory = new AgentFactory();
        Customer agent = agentFactory.createCustomer("A", "Java技术栈代理商");
        CustomerExt agentExt = agentFactory.createCustomerExt();
        System.out.println(agent);
        System.out.println(agentExt);
    }

}

输出结果:

可以看出,抽象工厂和工厂方法十分类似,只不过抽象工厂里面只生产一个对象,而抽象工厂可以生产多个对象。

抽象工厂缺点也很明显,第一就是和工厂方法一样工厂类非常多,第二就是扩展非常麻烦,比如我现在要为每个客户类型再加一份客户特殊资料,那所有涉及到抽象工厂的工厂类都要改,是不是要疯了。。

总结

如果有多个属于同一种类型的类,可以考虑使用工厂模式,统一提供生成入口,能从一定程度上解耦,扩展方便,也不用再到处 new 对象了。

但话又说回来,从示例可以看出,如果使用或者设计不当也会带来维护上的工作量。

本节教程所有实战源码已上传到这个仓库:

https://github.com/javastacks/javastack

好了,今天的分享就到这里了,后面栈长我会更新其他设计模式的实战文章,公众号Java技术栈第一时间推送。Java技术栈《设计模式》系列文章陆续更新中,请大家持续关注哦!

最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。

版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重大家的劳动成果和知识产权,抄袭必究。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2021最新版)

2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!


推荐阅读
  • Java如何导入和导出Excel文件的方法和步骤详解
    本文详细介绍了在SpringBoot中使用Java导入和导出Excel文件的方法和步骤,包括添加操作Excel的依赖、自定义注解等。文章还提供了示例代码,并将代码上传至GitHub供访问。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 【MicroServices】【Arduino】装修甲醛检测,ArduinoDart甲醛、PM2.5、温湿度、光照传感器等,数据记录于SD卡,Python数据显示,UI5前台,微服务后台……
    这篇文章介绍了一个基于Arduino的装修甲醛检测项目,使用了ArduinoDart甲醛、PM2.5、温湿度、光照传感器等硬件,并将数据记录于SD卡,使用Python进行数据显示,使用UI5进行前台设计,使用微服务进行后台开发。该项目还在不断更新中,有兴趣的可以关注作者的博客和GitHub。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Git GitHub多人协作
    在学校做一个小项目需要多人协作,就用到了gitHub,百度了一下多数写得乱七八糟或者支离破碎,于是总结了一下自己的步骤如下,第一次使用GitHUb,哪里不对望大神指出一.前期准备: ... [详细]
  • git向远程仓库提交遇到的各种坑
    **码云***gitinit初始化本地仓库gitinit*gitremoteaddorigingitgithub.com:xueqinngchennLocal-agent.git将 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
  • 初始化初始化本地空版本库,仓库,英文名repositorymkdirtest&&cdtestgitinit克隆项目到本地gitclone远程同 ... [详细]
  • 先记住几个专用名词,如下:Workspace:工作区IndexStage:暂存区Repository:仓库区(或本地仓库)Remote:远程仓库一、新建代码库#在当前目录新建一个G ... [详细]
author-avatar
莫念暧昧_zTq
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有