面试官问你工厂模式、抽象工厂模式? 你问要不要给他介绍个对象!!

88 阅读9分钟

学习的时候我们可以过度设计,只是为了使用一下设计模式,但是我们在工作中千万不要过度设计,什么东西都不能过度。

工厂系列有2种类设计模式:工厂方法、抽象工厂

但是平时也会有人提到什么简单工厂、静态工厂你强行说这是设计模式也行,模式嘛都是人定义的,但是一般技术上没有把他俩算在设计模式里。

为什么要是用工厂呢,直接new 他不香吗?

因为工厂可以灵活控制生产过程、可以限制权限、记录创建日志、添加修饰等等

一、故事起源

想一个故事,带入一下...

话说,有一个小伙子小浪浪,他呢是同龄人中混的比较好的,有房有车有存款。

他呢准备找对象,我们这里定义几种女生类型不同的类型有不同的爱好。

分别是:喜欢脱口秀的、喜欢哲学的、喜欢历史的

我们先创建这几种女生类:

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TalkShowGirl {
    public void love(){
        System.out.println("喜欢脱口秀");
    }
}

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class PhilosophyGirl {
    public void love(){
        System.out.println("喜欢哲学");
    }
}

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class HistoryGirl {
    public void love(){
        System.out.println("喜欢历史");
    }
}

然后小浪浪开始思考自己喜欢哪种:

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TestDemo {
    public static void main(String[] args) {
        // 刚开始准备找个喜欢脱口秀的
        TalkShowGirl girl = new TalkShowGirl();
        girl.love();

        // 男人嘛朝三暮四  后来想找个喜欢哲学的
        PhilosophyGirl girlp = new PhilosophyGirl();
        girlp.love();

        // 最后觉得还是找个喜欢历史的吧
        HistoryGirl girlh = new HistoryGirl();
        girlh.love();
    }
}

上边这种写法就是最原始的写法,自己想要什么就自己去new,这也是程序员经常开玩笑说的一句话:

没对象? 自己new 一个呀 !!

尴尬又不失礼貌的漏出想弄死对方的心情。

二、 简单工厂

上边说了,简单工厂能真正意义上说是一种设计模式,但是这也是一种把new对象动作整合的一种方式

这时候来了一个媒婆,媒婆跟小浪浪说了,你想要什么直接给我说,我给你找就可以。

对于媒婆来说给小浪浪找的都是姑娘(小浪浪暂时没啥问题,还是比较喜欢小姐姐多一些)

所以这时候姑娘需要抽象出一个概念来

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public interface Girl {
    void love();
}

那三中类型的姑娘都需要实现这个接口,也就是说虽然他们有不同的爱好,但是他们始终是个姑娘

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TalkShowGirl  implements Girl {
    @Override
    public void love(){
        System.out.println("喜欢脱口秀");
    }
}
//-----------------------------------------------------------
package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class PhilosophyGirl  implements Girl {
    @Override
    public void love(){
        System.out.println("喜欢哲学");
    }
}
//-------------------------------------------------------------
package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class HistoryGirl implements Girl {
    @Override
    public void love(){
        System.out.println("喜欢历史");
    }
}

媒婆类:

媒婆类其实就脱离了创建者,也就是脱离了小浪浪,媒婆可以在给小浪浪介绍姑娘之前先对姑娘进行一些个操作。

比如:化化妆、教她一些该说不该说的话、媒婆自己记录一下这个姑娘已经给多少人介绍过了等等。

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class SimpleFactory {
    public Girl find(String type) throws Exception {
        if (type == null || "".equals(type)) {
            throw new Exception("你什么也不说,我也没办法呀!!");
        }
        //jdk1.7开始switch支持String了 type不能为null 否则会报空指针异常
        switch (type) {
            case "history":
                // 工厂的好处就是 在创建之前可以随意加代码 也可以对HistoryGirl进行包装 
                // 比如媒婆给小姑娘化个妆 
                return new HistoryGirl();
            case "talkshow":
                return new TalkShowGirl();
            case "philosophy":
                return new PhilosophyGirl();
            default:
                throw new Exception("你要的可真多,我没有,滚!");
        }
    }
}

有了媒婆之后,小浪浪再找对象流程就变成这样了:

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TestDemo02 {
    public static void main(String[] args) throws Exception {
        // 请媒婆出山
        SimpleFactory factory =  new SimpleFactory();
        // 刚开始准备找个喜欢脱口秀的
        Girl girl = factory.find("talkshow");
        girl.love();

        // 男人嘛朝三暮四  后来想找个喜欢哲学的
        Girl girlp = factory.find("philosophy");
        girlp.love();

        // 最后觉得还是找个喜欢历史的吧
        Girl girlh = factory.find("history");
        girlh.love();

        // 过了段时间想要一个身材妖娆的  媒婆直接翻脸 让他滚
        Girl girls = factory.find("身材妖娆");
        girls.love();
    }
}

三、静态工厂

静态工厂就是媒婆电话公告天下,不用请媒婆出山,直接电话联系就能搞定

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class SimpleStaticFactory {
    public static Girl find(String type) throws Exception {
        if (type == null || "".equals(type)) {
            throw new Exception("你什么也不说,我也没办法呀!!");
        }
        //jdk1.7开始switch支持String了 type不能为null 否则会报空指针异常
        switch (type) {
            case "history":
                return new HistoryGirl();
            case "talkshow":
                return new TalkShowGirl();
            case "philosophy":
                return new PhilosophyGirl();
            default:
                throw new Exception("你要的可真多,我没有,滚!");
        }
    }
}

// 让媒婆找对象就直接
SimpleStaticFactory.find("xxxx") 

四、工厂模式

这个才是真正的被收纳到23中设计模式的其中一种

随着互联网发展,出来一个一个概念叫垂直领域,媒婆也与时俱进了,不同爱好的姑娘有不同的媒婆负责接洽。

你想找喜欢历史的姑娘,你就要找到负责喜欢历史姑娘的媒婆,如果你找别的媒婆那给你介绍的可不一定是你喜欢的哦。

其实就是各司其职:简单工厂违反了“开闭原则” (对修改关闭,对扩展开放),因为你要加一种类型的姑娘,你就需要修改媒婆的记事本SimpleFactory

这时候需要很多媒婆了,各种类型的媒婆都叫媒婆,所以他们应该有一个抽象接口(抽象类也可以)

package factory.simple;
// 抽象的媒婆
public interface Factory {
    Girl find();
}
//------------------------------
package factory.simple;
// 掌握喜欢脱口秀姑娘名单的媒婆
public class TalkShowFactory implements Factory{
    @Override
    public Girl find(){
        return new TalkShowGirl();
    }
}
//--------------------------------
package factory.simple;
// 掌握喜欢哲学的姑娘名单的媒婆
public class PhilosophyFactory  implements Factory{
    @Override
    public Girl find(){
        return new PhilosophyGirl();
    }
}
//--------------------------------
package factory.simple;
// 掌握喜欢历史的姑娘名单的媒婆
public class HistoryFactory implements Factory{
    @Override
    public Girl find(){
        return new HistoryGirl();
    }
}

这个时候小浪浪再找对象就需要根据自己的要求去找不同的媒婆了:

package factory.simple;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class TestDemo03 {
    public static void main(String[] args) {
        // 刚开始准备找个喜欢脱口秀的
        Factory mp = new TalkShowFactory();
        Girl girl = mp.find();
        girl.love();

        // 男人嘛朝三暮四  后来想找个喜欢哲学的
        Factory mpp = new PhilosophyFactory();
        Girl girlp = mpp.find();
        girlp.love();

        // 最后觉得还是找个喜欢历史的吧
        Factory mph = new HistoryFactory();
        Girl girlh = mph.find();
        girlh.love();

    }
}

这个时候如果小浪浪想找一个纯欲类型女生,我们不需要修改之前的三中类型媒婆,我们面向扩展开放,新建一种类型媒婆即可

file

package factory.simple;
public class SimpleSexyGirl implements Girl{

    @Override
    public void love() {
        System.out.println("哥哥,我美吗~");
    }
}
package factory.simple;
// 掌握纯欲姑娘信息的媒婆
public class SimpleSexyFactory implements Factory{
    @Override
    public Girl find(){
        return new SimpleSexyGirl();
    }
}

这就是面向扩展开放,我们新增的类型,不会影响之前的代码

五、抽象工厂

挠挠头,想想这个故事怎么强行适配一下抽象工厂。。

思考中.....

抽象工厂模式为创建一组对象提供解决方案。与工厂方法模式相比,抽象工厂模式的具体工厂不只是创建一种产品,他负责创建一族产品

是这样的,小浪浪虽然很优秀,但是他很懒,他让媒婆介绍对象的时候,还让媒婆把见面地点和去见面地点的交通方式都给安排好了。

这个时候媒婆的职责就变多了:安排相亲姑娘、安排见面地点、安排出行交通方式‘

我们画一个简单的图(千万不要迷失在网上那些看似很严谨又很专业的图中)

file

file

package factory.abstractfactorytest.girl;
public interface Girl {
    void love();
}
//------------哲学------------
package factory.abstractfactorytest.girl;
public class PhilosophyGirl  implements Girl {

    @Override
    public void love() {
        System.out.println("喜欢哲学");
    }
}
//--------纯欲-------------------
package factory.abstractfactorytest.girl;
public class SimpleSexyGirl implements Girl {

    @Override
    public void love() {
        System.out.println("哥哥,我美吗~");
    }
}

见面地点:

package factory.abstractfactorytest.address;
public interface Address {
    void where();
}
//---------图书馆-----------
package factory.abstractfactorytest.address;
public class BookAddress implements Address{

    @Override
    public void where() {
        System.out.println("图书馆见面");
    }
}
//-----------宾馆-----------------
package factory.abstractfactorytest.address;
public class HotelAddress implements Address{

    @Override
    public void where() {
        System.out.println("宾馆见面");
    }
}

见面出行方式:

package factory.abstractfactorytest.travelmodel;
public interface TravelModel {
    void run();
}
//----------开法拉利-------------------
package factory.abstractfactorytest.travelmodel;
public class FerrariTravelModel implements TravelModel{

    @Override
    public void run() {
        System.out.println("开法拉利去见面");
    }
}
//-----------步行去------------------
package factory.abstractfactorytest.travelmodel;
public class WalkTravelModel implements TravelModel{

    @Override
    public void run() {
        System.out.println("走着去见面");
    }
}

媒婆:

package factory.abstractfactorytest;
import factory.abstractfactorytest.address.Address;
import factory.abstractfactorytest.girl.Girl;
import factory.abstractfactorytest.travelmodel.TravelModel;
public abstract class AbstractFactory {
    // 找女孩
    abstract Girl find();
    // 找见面地点
    abstract Address address();
    // 找出行方式
    abstract TravelModel travelModel();
}

//------------------- 媒婆一号  哲学+图书馆+步行
package factory.abstractfactorytest;
import factory.abstractfactorytest.address.Address;
import factory.abstractfactorytest.address.BookAddress;
import factory.abstractfactorytest.girl.Girl;
import factory.abstractfactorytest.girl.PhilosophyGirl;
import factory.abstractfactorytest.travelmodel.TravelModel;
import factory.abstractfactorytest.travelmodel.WalkTravelModel;

public class Factory01 extends AbstractFactory{
    // 哲学女孩
    @Override
    public Girl find() {
        return new PhilosophyGirl();
    }
    // 图书馆
    @Override
    public Address address() {
        return new BookAddress();
    }
    // 步行见面
    @Override
    public TravelModel travelModel() {
        return new WalkTravelModel();
    }
}

//------------------ 媒婆二号 纯欲+宾馆+法拉利
package factory.abstractfactorytest;

import factory.abstractfactorytest.address.Address;
import factory.abstractfactorytest.address.HotelAddress;
import factory.abstractfactorytest.girl.Girl;
import factory.abstractfactorytest.girl.SimpleSexyGirl;
import factory.abstractfactorytest.travelmodel.FerrariTravelModel;
import factory.abstractfactorytest.travelmodel.TravelModel;

public class Factory02 extends AbstractFactory{

    //纯欲女孩
    @Override
    public Girl find() {
        return new SimpleSexyGirl();
    }

    // 宾馆
    @Override
    public Address address() {
        return new HotelAddress();
    }

    // 开法拉利见面
    @Override
    public TravelModel travelModel() {
        return new FerrariTravelModel();
    }
}

小浪浪找对象:

package factory.abstractfactorytest;

import factory.abstractfactorytest.address.Address;
import factory.abstractfactorytest.girl.Girl;
import factory.abstractfactorytest.travelmodel.TravelModel;

/**
 * @author 发现更多精彩  关注公众号:木子的昼夜编程
 */
public class Test {
    public static void main(String[] args) {
        // 找了个媒婆给介绍对象 媒婆给组合了一套
        AbstractFactory ab =  new Factory01();
        Girl girl01 = ab.find();
        Address address01 = ab.address();
        TravelModel travelModel01 = ab.travelModel();
        girl01.love();
        address01.where();
        travelModel01.run();

        // 上一个没有满意 又想换一个
        System.out.println("======================换一个");
        AbstractFactory ab2 =  new Factory02();
        Girl girl02 = ab2.find();
        Address address02 = ab2.address();
        TravelModel travelModel02 = ab2.travelModel();
        girl02.love();
        address02.where();
        travelModel02.run();
    }
}

输出结果:
喜欢哲学
图书馆见面
走着去见面
======================换一个
哥哥,我美吗~
宾馆见面
开法拉利去见面    

六、唠唠

设计模式是用来帮助我们写更漂亮的代码,而不是让我们来写更别扭的代码。

千万不能为了设计而设计,我的朋友小浪浪曾经说过我,你这样写就是过度设计。

有时候过度设计不仅费脑子而且还费时间


欢迎关注公众号:木子的昼夜编程