2.工厂模式

工厂模式是一种创建型模式。一般情况下细分为简单工厂模式、工厂方法模式和抽象工厂模式三种,简单工厂和工厂方法使用场景更多,抽象工厂原理相对前两种稍微复杂一点,并且在实际项目中不常使用,这里不过多介绍

简单工厂(Simple Factory)

简单工厂模式也称为静态工厂方法模式。在简单工厂模式中,有一个工厂类提供一个静态方法,XxFactory.createXxx(String type),通过参数类型来控制实例对象的创建。主要有三个成员

  • 抽象产品:抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口,例如IdentityProvider

  • 具体产品:具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例,例如AliPayIdentityProvider

  • 工厂类:负责实现创建所有实例的内部逻辑,例如IdentityFactory

工厂模式的UML图如下

通过一个例子来讲解,我们根据文件后缀(json、xml、thrift、java),选择不同的解析器(JsonParser、XmlParser、ThriftParser、JavaParser)来解析不同文件,示例代码如下

public class FileResource {
    public FileResource() {
    }

    public FileContent load(String filePath) throws FileNotFoundException {
        String fileExtension = getFileExtension(filePath);
        FileParser parser=createParser(fileExtension);

        String textPath = "";
        FileContent fileContent = parser.parse(textPath);
        return fileContent;
    }

    // 解析文件扩展名
    private String getFileExtension(String filePath) {
        if (filePath.endsWith("json")) {
            return "json";
        } else if (filePath.endsWith("java")) {
            return "java";
        } else if (filePath.equalsIgnoreCase("xml")){
            return "xml";
        }
        else {
            return null;
        }
    }

    private static FileParser createParser(String fileType) throws FileNotFoundException {
        if ("json".equalsIgnoreCase(fileType)) {
            return new JsonParser();
        } else if ("xml".equalsIgnoreCase(fileType)) {
            return new XmlParser();
        } else {
            throw new FileNotFoundException("current file type is not support");// 返回自定义异常/null,返回null 使用时需要判空
        }
    }
}

public abstract class FileParser {
    abstract FileContent parse(String Text);
}

public class JavaParser extends FileParser {
    @Override
    FileContent parse(String Text) {
        return null;
    }
}

public class JsonParser extends FileParser {
    FileContent fileContent;

    public JsonParser() {
        this.fileContent = new FileContent();
    }

    @Override
    FileContent parse(String Text) {
        //根据路径,解析出相关字段
        fileContent.setName("zhangsan");
        fileContent.setAge(19);
        fileContent.setDept("testDept");
        return fileContent;
    }
    
}

public class XmlParser extends FileParser {
    @Override
    FileContent parse(String Text) {
        return null;
    }
}

@Data
public class FileContent {
    String name;
    int age;
    String dept;
}

为了让类的职责更加单一,我们可以将createParser()函数封装到一个单独的类中,让这个类只负责对象的创建,这个类就是简单工厂模式类。大部分工厂模式类都以Factory结尾,工厂类中创建对象都以create开头,示例代码如下

public class FileParserFactory {
  	// 静态工厂方法,根据fileType 选择需要创建的工厂实例对象
    public static FileParser createParser(String fileType) throws FileNotFoundException {
        if ("json".equalsIgnoreCase(fileType)) {
            return new JsonParser();
        } else if ("xml".equalsIgnoreCase(fileType)) {
            return new XmlParser();
        } else {
            throw new FileNotFoundException("current file type is not support");// 返回自定义异常/null,返回null 使用时需要判空
        }
    }
}

在上面的代码实现中,每次调用 FileParserFactory.createParser()的时候,都需要创建一个新的parser。为了节约内存和对象创建的时间,可以将parser先创建好,使用时直接从缓存中取出parser对象,FileParserFactory可以优化如下:

public class FileParserFactory {
    private static Map<String,FileParser> cachedParser = new HashMap();

    static {
        cachedParser.put("java",new JavaParser());
        cachedParser.put("json",new JsonParser());
        cachedParser.put("xml",new XmlParser());
    }

    public static FileParser createParser(String fileType) throws FileNotFoundException {
        if (fileType==null||fileType.isEmpty()){
            return null;
        }

        FileParser fileParser=cachedParser.get(fileType);
        return fileParser;
    }
}

我们已经完成了一个简单工厂模式的示例,简单总结下它的优缺点

优点

  • 实现对职责的分离,提供了专门工厂类用于创建实例

  • 不用关心具体的判断逻辑(封装在共厂类中),不用关心创建具体产品类的逻辑,调用方决定在什么时候创建哪一类实例

  • 可以在不修改调用方代码的基础上,新增具体对象,提高了系统的灵活性

不足

  • 违反开闭原则,一旦添加新产品就不得不修改工厂类的逻辑,容易造成错误

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响

  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构

public class FileParserFactory {
    public static FileParser createParser(String fileType) throws FileNotFoundException {
        if ("json".equalsIgnoreCase(fileType)) {
            return new JsonParser();
        } else if ("xml".equalsIgnoreCase(fileType)) {
            return new XmlParser();
        } 
      // 新增判断,每新增一个类型,就要加一个判断
      else if ("properites".equalsIgnoreCase(fileType)) {
            return new PropertiesParser();
        } else {
            throw new FileNotFoundException("current file type is not support");// 返回自定义异常/null,返回null 使用时需要判空
        }
    }
}

为工厂类创建简单工厂,工厂类负责各个工厂类对象的创建,FileParserFactoryMap工厂类负责JsonParserFactory、XmlParserFactory等创建,如果需要新增一种文件类型只要新增xxparser类和xxxParserFactory,在FileParserFactoryMap工厂类中添加xxxParserFactory,代码改动很少,基本符合开闭原则

工厂方法(Factory Method)

为工厂类创建简单工厂,工厂类负责各个工厂类对象的创建,FileParserFactoryMap工厂类负责JsonParserFactory、XmlParserFactory等创建,如果需要新增一种文件类型只要新增xxparser类和xxxParserFactory,在FileParserFactoryMap工厂类中添加xxxParserFactory,代码改动很少,基本符合开闭原则

工厂方法 UML图

public class FileResource {
    public FileResource() {
    }

    public FileContent load(String filePath) throws FileNotFoundException {
        String fileExtension = getFileExtension(filePath);
        FileParserFactory parserFactory = FileParserFactoryMap.getParserFactory(fileExtension);
        FileParser parser = parserFactory.createParser();

        String textPath = "";
        FileContent fileContent = parser.parse(textPath);
        return fileContent;
    }

    // 解析文件扩展名
    private String getFileExtension(String filePath) {
        if (filePath.endsWith("json")) {
            return "json";
        } else if (filePath.endsWith("java")) {
            return "java";
        } else if (filePath.endsWith("xml")) {
            return "xml";
        } else {
            return null;
        }
    }
}

public interface FileParserFactory {
    FileParser createParser();
}

public class JsonParserFactory implements FileParserFactory {
    @Override
    public FileParser createParser() {
        return new JsonParser();
    }
}

public class XmlParserFactory implements FileParserFactory {
    @Override
    public FileParser createParser() {
        return new XmlParser();
    }
}

public class FileParserFactoryMap {
    private static Map<String, FileParserFactory> cachedFactories = new HashMap<>();

    static {
        cachedFactories.put("json", new JsonParserFactory());
        cachedFactories.put("xml", new XmlParserFactory());
    }

    // 返回缓存好的单例对象
    public static FileParserFactory getParserFactory(String fileType) {
        if (fileType == null || fileType.isEmpty()) {
            return null;
        }

        FileParserFactory parserFactory = cachedFactories.get(fileType);
        return parserFactory;
    }
}

public class JsonParser extends FileParser {
    FileContent fileContent;

    public JsonParser() {
        this.fileContent = new FileContent();
    }

    @Override
    FileContent parse(String Text) {
        //根据路径,解析出相关字段
        fileContent.setName("zhangsan");
        fileContent.setAge(29);
        fileContent.setDept("testDept");
        return fileContent;
    }

}

public class XmlParser extends FileParser {
    private FileContent fileContent;

    XmlParser() {
        this.fileContent = new FileContent();
    }
    @Override
    FileContent parse(String Text) {
        fileContent.setName("test xmlParser");
        return fileContent;
    }
}

我们也已经完成了一个工厂方法模式的示例,简单总结下它的优缺点

优点

  • 工厂方法模式的扩展性非常强,在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,而只要添加一个具体工厂和具体产品类,如果需要新增一种文件类型只要新增xxparser类和xxxParserFactory,在FileParserFactoryMap工厂类中添加xxxParserFactory,代码改动很少,基本符合开闭原则

  • 良好的封装性,代码结构清晰。调用者需要一个具体的产品对象时,只需要知道这个产品的类名就可以了,不需要知道具体的创建过程,降低的模块之间的耦合

  • 屏蔽产品类,产品类的实现如何变化,调用者不需要关系,它只关系产品的接口,只要接口保持不变,系统中的上层模块就不需要变化。所以工厂方法模式经常用来解耦,高层模块只需要知道产品的抽象类,实现类不需要关系,这符合迪米特法则,也符合依赖倒置原则

不足

  • 工厂模式需要额外创建诸多Factory类,也会增加代码的复杂性

  • 增加了开发量,在使用简单工厂模式时,我们只想要添加一个case分支,现在则需要创建类

  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度

那什么时候该用工厂方法模式,而非简单工厂模式呢?

之所以将某个代码块剥离出来,独立为函数或者类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护,基于这个设计思想

  • 每个Factory类只是做简单的new操作,代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的函数或者类,也没必要设计成独立的类,所以,在这个应用场景下,简单工厂模式简单好用,比工厂方法模式更加合适。

  • 当对象的创建逻辑比较复杂,不只是简单的new一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂。

  • 除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同的对象。如果我们使用简单工厂模式来实现,就只能选择第一种包含if分支逻辑的实现方式。如果我们还想避免烦人的if-else分支逻辑,这个时候,我们就推荐使用工厂方法模式。

附录

工厂模式,从第三方登录说起

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值