前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式---抽象工厂模式

设计模式---抽象工厂模式

作者头像
技术文章精选
发布2022-11-08 16:37:51
2930
发布2022-11-08 16:37:51
举报
文章被收录于专栏:优质文章优质文章

简述

  • 类型:创建型
  • 目的:实现对客户端中对象族的平替对象族 具有共同主题的一组对象的集合。比如,华为的手机,笔记本,平板可以统称为华为族。

我们借以下案例来说说如何使用抽象工厂模式平替对象族。

优化案例

最初版

代码语言:javascript
复制
// 上传模块的接口public interface Uploader {    void upload(String fileName);} // 下载模块的接口public interface Downloader {    void download(String fileName);} // Linux环境下的上传实现public class LinuxUploader implements Uploader {    public void upload(String fileName) {        System.out.printf("[Linux]正在上传%s...", fileName);    }} // Linux环境下的下载实现public class LinuxDownloader implements Downloader {    public void download(String fileName) {        System.out.printf("[Linux]正在下载%s...", fileName);    }} // Unix环境下的上传实现public class UnixUploader implements Uploader {    public void upload(String fileName) {        System.out.printf("[Unix]正在上传%s...", fileName);    }} // Unix环境下的下载实现public class UnixDownloader implements Downloader {    public void download(String fileName) {        System.out.printf("[Unix]正在下载%s...", fileName);    }}

客户端调用如下。

代码语言:javascript
复制
public class Client {    public static void main(String[] args) {        Uploader uploader = new LinuxUploader();        Downloader downloader = new LinuxDownloader();    }}

传统是new创建对象的方式有着硬编码的问题。当我们需要把所有LinuxXXX对象改为UnixXXX对象时,就必须在项目中检索所有的LinuxXXX修改为UnixXXX。这无疑增加了大量的无意义的工作。

修改版v1(简单工厂)

增加一个工厂类,其他不变。

代码语言:javascript
复制
// 简单工厂public class Factory {    static Uploader uploader(String target) {        if ("LinuxUploader".equals(target)) {            return new LinuxUploader();        } else if ("UnixUploader".equals(target)) {            return new UnixUploader();        }         throw new Exception("输入的参数错误");    }        static Downloader downloader(String target) {        if ("LinuxDownloader".equals(target)) {            return new LinuxDownloader();        } else if ("UnixDownloader".equals(target)) {            return new UnixDownloader();        }        throw new Exception("输入的参数错误");    }}

修改后,客户端的代码调用。

代码语言:javascript
复制
public class Client {    public static void main(String[] args) {        Uploader up1 = Factory.instance("LinuxUploader");        Downloader down1 = Factory.instance("LinuxDownloader");        Uploader up2 = Factory.instance("UnixUploader");        Downloader down2 = Factory.instance("UnixDownloader");    }}

在一定程度上解决了客户端硬编码问题。并且当我们需要把所有LinuxUploader对象改为UnixUploader对象时,只需要在Factory中将new LinuxUploader() → new UnixUploader()即可。这无疑节省了很多的时间,也无需为硬编码带来的大量改修而苦恼。

但是目前这个优化方案依然有至少两个问题,一是Factory.uploader方法中耦合了所有的Uploader实现类,这可能有碍于未来的项目维护,二是new LinuxUploader() → new UnixUploader()这种修改方式会导致代码目的不明确,既然不论是LinuxUploader还是UnixUploader都直接生成UnixUploader对象,就没有必要定义LinuxUploader了呀。实际上是因为客户端代码中还有使用OSFactory.instance("LinuxUploader")来创建的对象,为了不修改客户端代码,强行做如上修改。

修改版v2(抽象工厂)

将原本的工厂类抽象化,并定义一系列不同的实现类,其余不变。

代码语言:javascript
复制
// 抽象的工厂接口public interface Factory {   	Uploader uploader();        Downloader downloader();} // Linux环境下的工厂实现public class LinuxFactory {    public Uploader uploader() {        return new LinuxUploader();    }        public Downloader downloader() {        return new LinuxDownloader();    }} // Uinx环境下的工厂实现public class UnixFactory {    public Uploader uploader() {        return new UnixUploader();    }        public Downloader downloader() {        return new UnixDownloader();    }}

修改后,客户端的代码调用。

代码语言:javascript
复制
public class Client {    private static Factory factory = new LinuxFactory();        public static void main(String[] args) {        Uploader uploader = factory.uploader();        Downloader downloader = factory.downloader();    }}

将原本Factory类中臃肿的逻辑分散到各个子类中,提高了系统的可维护性,不用再每次都修改Factory类了。

那么,问题来了,这样的结构对于我们的项目有什么帮助吗?有,而且很大,在客户端定义了一个静态的属性factory,当接下来客户换了系统从Linux换到了Unix,那我们也需要更换对应的上传下载的类,这时我们只要修改factory引用的具体工厂类的对象就可以了,很方便。实际上还可以更加方便的实现对象族的平替。而为了实现这个需求,我们需要结合Java反射这项技术。请看下面的代码。

修改版v3(抽象工厂+反射)

只修改客户端的调用方式,其他位置不做修改。

代码语言:javascript
复制
public class Client {    private static Factory factory;        // 通过Java的反射机制创建上述factory对象    static {        // 读取prop配置文件        Properties prop = new Properties();        FileReader fileReader = new FileReader("src/resource/props/config.prop");        prop.load(fileReader);        fileReader.close();        factory = (Factory) Class.forName(prop.getProperty("FACTORY"))                                   .getDeclaredConstructor().newInstance();    }        public static void main(String[] args) {        Uploader uploader = factory.uploader();        Downloader downloader = factory.downloader();    }}

增加一个properties文件文件,定义如下。

代码语言:javascript
复制
#当前使用的工厂类FACTORY=design.abstractfactory.LinuxFactory

当需要将系统中的Linux系列的对象转化为Unix系列时,只需要更改上述配置文件即可,具体如下。

代码语言:javascript
复制
#当前使用的工厂类FACTORY=design.abstractfactory.UnixFactory

总之就是非常方便。

总结

优点

  • 轻松做到对象族的平替。

缺点

  • 类数量倍增,系统复杂度增加。

应用场景

  • 根据需求,需要全面替换系统中的某个对象族时。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 简述
  • 优化案例
    • 最初版
      • 修改版v1(简单工厂)
        • 修改版v2(抽象工厂)
          • 修改版v3(抽象工厂+反射)
          • 总结
            • 优点
              • 缺点
                • 应用场景
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档