设计模式之工厂模式

简介: 工厂模式与单例模式一样,工厂模式同样属于创建型设计模式的一种。单例模式用于保证一个类只有一个实例,而工厂模式则用于创建类型相关的不同对象,它同样具有不同的实现方式,具体可以细分为简单工厂、工厂方法、抽象工厂,分别适用于不同的场景。

工厂模式


与单例模式一样,工厂模式同样属于创建型设计模式的一种。单例模式用于保证一个类只有一个实例,而工厂模式则用于创建类型相关的不同对象,它同样具有不同的实现方式,具体可以细分为简单工厂、工厂方法、抽象工厂,分别适用于不同的场景。


简单工厂


我们项目中经常会做一些报警相关的功能,报警有可以分为短信报警、语音报警、邮箱报警等各种方式,接到这个需求之后我们使用在最简单的方式实现如下。


public class Service {
    public void doSomething() {
        // 省略业务代码...
        String type = "..";
        Alert alert = new Alert();
        if ("sms".equals(type)) {
            alert.sendSms();
        } else if ("email".equals(type)) {
            alert.sendEmail();
        } else if ("voice".equals(type)) {
            alert.sendVoice();
        }
        // 省略业务代码
    }
}
public class Alert {
    public void sendSms(){
    }
    public void sendEmail(){
    }
    public void sendVoice(){
    }
}


为了保证业务逻辑中不夹杂太多的报警代码,我们把不同类型报警的代码分别抽象到了报警类的方法中,但是每次新增一种报警类型仍需要新增一种方法,然后修改业务类,如果报警的逻辑比较复杂还是会导致报警类的代码量不断增加,为了解决报警类复杂的问题,我们可以为报警抽象出一个接口,改动后的代码如下。


public class Service {
    public void doSomething() {
        // 省略业务代码...
        String type = "..";
        Alert alert = null;
        if ("sms".equals(type)) {
            alert = new SmsAlert();
        } else if ("email".equals(type)) {
            alert = new EmailAlert();
        } else if ("voice".equals(type)) {
            alert = new VoiceAlert();
        }
        alert.send();
        // 省略业务代码...
    }
}
public interface Alert {
    void send();
}
public class SmsAlert implements Alert{
    @Override
    public void send() {
    }
}
public class EmailAlert implements Alert{
    @Override
    public void send() {
    }
}
public class VoiceAlert implements Alert{
    @Override
    public void send() {
    }
}


此时,如果增加新的报警类型,新增对应的子类然后修改业务代码即可。虽然我们已经解决了报警类代码复杂的问题,但创建报警实例的代码仍然耦合在业务代码中,如果类型比较多的话就会影响业务代码的可读性,为了让业务类的职责更单一、代码更清晰,我们可以很自然的可以将创建通知类的代码放到其他地方,修改后的代码如下。


public class Service {
    public void doSomething() {
        // 省略业务代码...
        String type = "..";
        Alert alert = AlertFactory.getAlert(type);
        alert.send();
        // 省略业务代码...
    }
}
public class AlertFactory {
    public static Alert getAlert(String type){
        Alert alert = null;
        if ("sms".equals(type)) {
            alert = new SmsAlert();
        } else if ("email".equals(type)) {
            alert = new EmailAlert();
        } else if ("voice".equals(type)) {
            alert = new VoiceAlert();
        }
        return alert;
    }
}


重构后的 AlertFactory 就是一个工厂类,#getAlert 方法就是工厂方法,这种设计模式被称为简单工厂,由于创建类实例的方法是静态的,因此简单工厂又被称为静态工厂。可以看到软件的发展是不断演进的,设计模式的提出就是为了解决特定的问题。使用简单工厂之后,将创建实例的代码从业务代码中隔离出来,使业务类符合单一职责原则,增加了代码了可读性、扩展性。


如果实例化的资源消耗比较大,并且实例化的对象是可以复用的,还有另一种方法实现简单工厂。


public class AlertFactory {
    private static Map<String, Alert> cache = new HashMap<>();
    static {
        cache.put("sms", new SmsAlert());
        cache.put("email", new EmailAlert());
        cache.put("voice", new VoiceAlert());
    }
    public static Alert getAlert(String type) {
        Alert alert = cache.get(type);
        return alert;
    }
}


工厂方法


对于上述的简单工厂来说,如果增加新的告警类型,仍需要对工厂类进行修改,如果改动不频繁的情况下稍微不满足开闭原则也是可以接受的。对于第一种简单工厂,如果一定要把 if 去掉那么可以利用多态的方式。


public interface AlertFactory {
    Alert getAlert(String type);
}
public class SmsAlertFactory implements AlertFactory{
    @Override
    public Alert getAlert(String type) {
        return new SmsAlert();
    }
}
public class EmailAlertFactory implements AlertFactory {
    @Override
    public Alert getAlert(String type) {
        return new EmailAlert();
    }
}
public class VoiceAlertFactory implements AlertFactory{
    @Override
    public Alert getAlert(String type) {
        return new VoiceAlert();
    }
}


我们通过将工厂类修改为接口,然后由每个具体的工厂来创建对象,这样如果增加新的报警类型就不需要对工厂类进行修改,相对简单工厂更符合开闭原则。这种工厂模式被成为工厂方法。

虽然增加新的报警类型工厂类不需要进行修改,但是使用方的代码确复杂了。


public class Service {
    public void doSomething() {
        // 省略业务代码...
        String type = "..";
        Alert alert = null;
        if ("sms".equals(type)) {
            alert = new SmsAlertFactory().getAlert(type);
        } else if ("email".equals(type)) {
            alert = new EmailAlertFactory().getAlert(type);
        } else if ("voice".equals(type)) {
            alert = new VoiceAlertFactory().getAlert(type);
        }
        alert.send();
        // 省略业务代码...
    }
}


又回到了最开始的代码,为了解决创建工厂对象的问题,我们还需要为工厂再创建一个工厂。


public class Service {
    public void doSomething() {
        // 省略业务代码...
        String type = "..";
        Alert alert = AlertFactoryMap.getAlertFactory(type).getAlert(type);
        alert.send();
        // 省略业务代码...
    }
}
public class AlertFactoryMap {
    private static Map<String, AlertFactory> cache = new HashMap<>();
    static {
        cache.put("sms", new SmsAlertFactory());
        cache.put("email", new EmailAlertFactory());
        cache.put("voice", new VoiceAlertFactory());
    }
    public static AlertFactory getAlertFactory(String type) {
        return cache.get(type);
    }
}


使用了工厂方法之后,项目中的类已经增加了很多,因此在大多数情况下,个人更推荐使用简单工厂。


抽象工厂


抽象工厂相对简单工厂、工厂方法来说,使用场景相对较少。对于上面报警的案例,我们需要配置不同的报警规则,如每分钟请求此时超过多少或者出现异常多少次进行报警;还可能分别将配置放在不同的地方,如本地文件、配置中心、zookpeer等;还可能使用不同的格式,如xml、json、properties、yaml等。如果我们还需要支持将配置放在不同的地方,这样就有不同的维度来对报警进行划分。支持本地文件和短信报警、支持配置中心和邮箱告警等等,组合方法就会越来越多,如果还使用简单工厂或工厂方法无法解决我们的问题,我们可以让我们的工厂同时支持多种创建多种维度的告警实例,具体如下。


public interface AlertFactory {
    Alert getLocalFileAlert();
    Alert getNacosAlert();
}
public class SmsAlertFactory implements AlertFactory{
    @Override
    public Alert getLocalFileAlert() {
        return new SmsLocalFileAlert();
    }
    @Override
    public Alert getNacosAlert() {
        return new SmsNacosAlert();
    }
}
public class EmailAlertFactory implements AlertFactory {
    @Override
    public Alert getLocalFileAlert() {
        return new EmailLocalFileAlert();
    }
    @Override
    public Alert getNacosAlert() {
        return new EmailNacosAlert();
    }
}


总结

工厂模式用于解决对象的创建问题。当使用 if else 创建相关类型的不同对象时可以改用工厂模式进行优化;当创建对象的逻辑比较复杂时同样可以将创建对象逻辑抽象到工厂中。使用工厂模式可以使类的职责更为清晰、封装创建对象的“变化”。

目录
相关文章
|
3月前
|
设计模式 存储 Java
设计模式-工厂模式
设计模式-工厂模式
57 1
|
3月前
|
设计模式
设计模式【二】:工厂模式
设计模式【二】:工厂模式
33 0
|
11月前
|
设计模式 Java C++
设计模式之工厂模式详解和应用
设计模式之工厂模式详解和应用
47 0
|
2月前
|
设计模式 XML 缓存
设计模式-工厂模式-1-2
【6月更文挑战第11天】工厂模式用于封装对象创建,但当load函数与工厂类耦合时,问题出现。为解决此问题,引入了工厂的工厂,如`RuleConfigParserFactoryMap`,它创建并缓存工厂对象,简化了代码修改。然而,过多的Factory类会增加复杂性,简单工厂模式在此类应用中更为适用。当对象创建逻辑复杂,需组装其他对象或避免if-else时,采用工厂方法模式。抽象工厂则处理多维度分类的对象创建。总结:工厂模式的核心价值在于封装变化、代码复用、隔离复杂性和控制复杂度,适用于创建逻辑复杂的情况,否则直接使用new操作即可。
22 5
|
9月前
|
设计模式 前端开发 Java
设计模式之工厂模式(1)
工厂模式是一种对象创建型模式,它提供了一种创建对象的最佳实践。在工厂模式中,我们在创建对象时不使用 new 关键字,而是通过调用工厂方法来创建对象。工厂方法是一种在子类中定义的方法,该方法负责实例化对象。工厂方法可以返回不同的对象类型,因此工厂模式可以创建一组相关或不相关的对象。这样就可以将对象的创建和使用解耦。
49 0
|
9月前
|
设计模式 Java C#
设计模式之工厂模式(2)
接着,我们定义一个抽象工厂类AbstractFactory,它有两个抽象方法createShape和createColor,分别返回一个Shape对象和一个Color对象: java
39 0
|
设计模式
设计模式之工厂模式
设计模式之工厂模式
55 0
|
设计模式
【设计模式】工厂模式
【设计模式】工厂模式
|
设计模式 SQL 搜索推荐
设计模式之工厂模式(二)
设计模式之工厂模式
102 0
|
设计模式 消息中间件 Java
一起来学设计模式之工厂模式
前言 目前正在出一个设计模式专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~ 本节给大家讲一下设计模式中的工厂模式~ 本专题的所有案例代码主要以Java语言为主, 好了, 废话不多说直接开整吧~ 工厂模式 工厂模式是一种常用的设计模式,它提供了一种创建对象的方式,该方式隐藏了对象创建的细节并提供了一个通用的接口,从而使得代码更加灵活和可扩展。在工厂模式中,我们将对象的创建过程封装到一个工厂类中,通过工厂类来创建具体的对象,这样客户端就不需要关心对象的创建过程,只需要调用工厂类提供的接口来获取所需的对象即可。