【设计模式】4——Factory Method(工厂)模式

时间: | 分类: 设计模式

》》目录《《

因为不是很能理解工厂模式的概念,于是拖了挺长时间的...(其实在摸鱼

工厂模式的话,个人感觉跟模板模式非常像,不太好区分这两个的区别,或者说Factory模式是基于Template模式的?

像之前Template method模式的例子(制造笔记本电脑的),其实也能看出一点问题,实现类是写死在main方法里的,并没有解耦。如果要生产新的笔记本电脑,就需要修改源码重新进行编译。

最近看到个很有意思的例子,不知道自己理解的对不对,如有错误欢迎指出

简单工厂/静态工厂

有个服务器监视脚本,定时给管理员发送服务器的信息(CPU、内存、磁盘占用率等),那么发送的话有几个方式,使用QQ发送,使用邮件发送,使用短信发送,如果使用单纯的template模式写,就是这样的:

//发送的接口
public interface IsendApi{
    public void sendInfo(String msg);
}

//短信Api类,实现IsendApi接口的发送信息方法
public class SMSApi implements IsendApi{
    public SMSApi(String url,String authkey,String signature){

    }
    @Override
    public sendInfo(String msg){
        //发送到指定手机
    }
}

//邮件Api类,发送到某人的邮箱
public class MailApi implements IsendApi{
    public MailApi(String url,String senderEmail,String authkey){

    }
    @Override
    public sendInfo(String msg){
        //发送到指定的邮箱
    }
}

//QQApi类,发送到某人的QQ
public class QQApi implements IsendApi{
    //构造函数
    public QQApi(long qq,long password,long targetQQ,String msg){

    }
    @Override
    public sendInfo(String msg){
        //发送到指定的QQ
    }
}

public class Main{
    public static void main(String[] args){
        //省略获取系统信息的代码

        //初始化api的构造函数(每个api需要的构造函数都不同)
        IsendApi api = new QQApi(19195654,"password123",12316465);//
        api.sendInfo(Infomation.getSysInfo());//实现类是QQApi,则发送信息到某人的QQ

    }
}

可以看到,虽然各个实现类都实现了这个IsendApi的接口,但是前期初始化需要做大量的准备工作,例如sms的api需要有authkey向短信提供商验证才能发送信息,而qq需要有qq号和密码登陆后才能发送信息,如果要更换实现类,虽然api接口定义的方法无需更新,但是由于调用了实现类的方法(构造函数)依然还是和实现类耦合,一旦需要更换实现方式,就需要重新编译这个类。

如果使用了工厂模式,可以这样写:

public class SenderFactory(){

    //TYPE可以用常量或者枚举,更方便使用,这里为了简化代码使用String字符串代替
    public IsendApi getSender(String type){
        switch(type){
            case "mail":
                return new MailApi("https://api.email.com","[email protected]","key");
            case "qq":
                return new QQApi(19195654,"password123",12316465);
            case "sms":
                return new SMSApi("https://api.sms.com","authkey","signature");
        }
    }
}

然后main改写成

public class Main{
    public static void main(String[] args){
        //省略获取系统信息的代码

        //初始化工厂类
        SenderFactory factory = new SenderFactory();
        //从配置文件中获取工厂所需要的类型(或者从其他地方也行,主要是为展示工厂方法动态获取实例使用)
        //然后由工厂动态返回一个具体的实现类,而不是硬编码指定
        IsendApi api = factory.getSender(Config.getSenderType());
        api.sendInfo(Infomation.getSysInfo());//实现类是QQApi,则发送信息到某人的QQ
    }
}

可以看到如果使用工厂的话,调用方就不再需要实例化具体的实现类,只需要由工厂返回所需的实现类进行实现就可以了。解耦了Main类和具体的Api实现类。

注:这个例子和书上的略有不同,书上是一个工厂的框架,由工厂和产品组成,实际实现类再继承框架的工厂和产品类去实现,书上的例子可能是要生成一个更加通用的类,而这个例子更像是一个简单工厂类(静态的工厂)。

这是一个静态工厂(使用public)/简单工厂的例子,会发现虽然解耦了Main类和具体实现类,但是工厂类和实现类依然是强耦合的。

使用标准的工厂模式:

//工厂接口(interface)
public interface IFactory{
    IsendApi getApi();//获取工厂
}

//SMS工厂,实现IFactory的getApi()方法
public class SMSFactory implements IFactory {
    //省略了构造函数
    public void IsendApi getApi(){
        return new SMSApi();//sms工厂返回smsApi对象
    }
}
//Email工厂,也实现IFactory的getApi()方法
public class MailFactory implements IFactory {
    //省略构造函数
    public void IsendApi getApi(){
        return new MailApi();//Mail工厂返回MailApi对象
    }
}
//QQ工厂
public class QQFactory implements IFactory {
    public QQFactory(String xxx,String xxxxxx){
        //可以定义自己的构造函数,给getApi使用
    }
    public void IsendApi getApi(){
        //省略了初始化所需的函数
        return new QQApi();//QQ工厂返回QQApi对象
    }
}

Main方法改写成

public class Main{
    public static void main(String[] args){
        //省略获取系统信息的代码

        //初始化工厂类
        IFactory factory;
        IsendApi api;
        factory = new QQFactory(xxx,xxx)//生成一个QQ工厂
        api = factory.getApi();
        api.sendInfo(Infomation.getSysInfo());//实现类是QQApi,则发送信息到某人的QQ

        factory = new MailFactory(xxx,xxx)//生成一个Mail工厂
        api = factory.getApi();
        api.sendInfo(Infomation.getSysInfo());//实现类是MailApi,则发送信息到某人的Mail
        //sms类似,不再赘述
    }
}

这样的工厂模式,就不再需要一个强耦合的工厂类,而是由客户端进行决定,当需要用到QQapi,就new一个QQapi的Factory(工厂只处理对应的产品,不再和其他产品耦合),如果需要使用Mail,将工厂替换为factory即可,未来如果有更多的需求,只要增加对应的产品和工厂方法,不需要修改已经写好的类(除了main方法,这里作为client)

简单工厂(静态工厂) 和 工厂模式的区别 (笔记,非结论)

简单工厂/工厂方法不属于23种设计模式之一,简单工厂将实例对象的生成和客户端分离,由工厂去实例化对象,解耦了Client和实例,但是当出现一个新的对象时就需要修改这个简单工厂,增加判断。违反了开放——闭合原则(对扩展开放,对修改关闭)

而工厂模式,可以是一个抽象类或者是接口,由子类override获取实现类的方法,对应的工厂返回对应的实例,工厂不再与其他的实例类耦合,有新的实例对象也只需要写新的工厂和对应的产品类,不需要修改原有的工厂类,但是需要由客户端决定使用哪个工厂类,然后再使用这个工厂。

注:但是个人感觉这和不使用工厂差不了太多?使用工厂模式只解了和对应实例的耦,但是并没有和工厂解耦。。更像只是把对应实例所需要的参数封装到工厂里?
看了一下,似乎工厂模式就是这一个效果?如果要解决这个问题,可能需要使用抽象工厂,和简单工厂的区别在于只是把判断从简单工厂交给了客户端来处理。
比如切换数据库类型,原本是实例化MysqlApi,OracleApi,如果使用工厂也还是需要用MysqlFactory,如果需要全局的替换那还是用全局变量方便吧?如果用全局变量那使用Factory还是子类也没有什么区别了....?
然后是对应实例的实例化是在工厂调用方法后,而简单工厂是在获取时就已经实例化了
工厂模式似乎就是为了解实现类的耦,在不知道具体实现类怎么生成的时候使用工厂,让工厂帮忙生产,调用方就不需要知道实例具体要怎么初始化,不过感觉还是需要多参数才能体现出工厂的优势(但是多参数的话在使用工厂的时候也需要提供吧?那还是得给工厂一堆参数。。。)

还有一个抽象工厂,未来会单独写一篇笔记。

未完待续...(18/12/07)


Java 设计模式



白咲美绘瑠's blog