工厂模式及模板方法模式深度思考

(注:代码在最底下的附件)
1.简单工厂
郭大神和盘哥在说好想买ipad,用代码实现一下
不用模式的设计
public interface Product {

    String getDesc();
}

public class IpadProduct implements Product {

    @Override
    public String getDesc() {
        return "Ipad2";
    }

}

public class Client {

    public static void main(String[] args) {
        Product product = new IpadProduct();
        System.out.println("郭大神说好想买" + product.getDesc() + ",但没钱!!!");
        product = new IpadProduct();
        System.out.println("盘哥说我好想买" + product.getDesc() + ",但没钱!!!");
    }
}

有何问题?
如果想买的iphone5时怎么办?
得找到所有new IpadProduct改成new IphoneProduct
而且改的是客户端的代码

应用模式的设计
public class IphoneProduct implements Product {

    @Override
    public String getDesc() {
        return "Iphone5";
    }
}
public class SimpleFactory {

    public static Product getProdcut() {
        return new IpadProduct();
    }
}

public class Client {

    public static void main(String[] args) {
        Product product = SimpleFactory.getProduct();
        System.out.println("郭大神说好想买" + product.getDesc() + ",但没钱!!!");
        product = SimpleFactory.getProduct();
        System.out.println("盘哥说我好想买" + product.getDesc() + ",但没钱!!!");
    }
}

无模式遇到的问题如何解决?
只要在SimpleFactory.getProdcut()换成IphoneProdcut即可
优点
创建对象收拢,扩展性加强了。
扩展时不需要修改客户端代码。
对象创建与使用解耦
缺点
多出一个工厂类

2.工厂方法


应用工厂方法模式的设计
abstract public class Person {
    private String name;

    Person(String name){
        this.name = name;
    }

    abstract Product getProduct();

    public void say() {
        Product product = getProduct();
        System.out.println(name + "说我好想买" + product.getDesc() + ",但没钱!!!");
    }
}

public class GuoPerson extends Person {
    public GuoPerson(){
        super("郭大神?);
    }

    @Override
    Product getProduct() {
        return new IpadProduct();
    }
}

public class ShenPerson extends Person {
    public ShenPerson(){
        super("盘哥");
    }

    @Override
    Product getProduct() {
        return new IpadProduct();
    }
}

public class ModeClient {

    public static void main(String[] args) {
        Person person = new GuoPerson();
        person.say();
        person = new ShenPerson();
        person.say();
    }
}

优点
可以在不知道具体产品下实现编程
可以在不改变业务代码上很容易扩展出一个新的产品
缺点
多出几个具体工厂类

3.抽象工厂
考虑一下生产ipad和iphone的一个程序
工厂模式的设计有何问题?

public interface Ipad extends Product {
}
public class HongKongIpad implements Ipad {
    @Override
    public String getDesc() {
        return "香港版的Ipad";
    }
}
public class USAIpad implements Ipad {
    @Override
    public String getDesc() {
        return "美国版的Iphone";
    }
}
public interface Iphone extends Product {
}
public class HongKongIphone implements Iphone {
    @Override
    public String getDesc() {
        return "香港版的Iphone";
    }
}
public class USAIphone implements Iphone {
    @Override
    public String getDesc() {
        return "美国版的Iphone";
    }
}
public class SimpleFactory {
    public static Ipad createIpad(int type) {
        if (type == 1) {
            return new HongKongIpad();
        }

        return new USAIpad();
    }

    public static Iphone createIphone(int type) {
        if (type == 1) {
            return new HongKongIphone();
        }

        return new USAIphone();
    }
}
public class Client {
    public static void main(String[] args) {
            Ipad ipad = SimpleFactory.createIpad(1);
            Iphone iphone = SimpleFactory.createIphone(1);
            System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc());
//            ipad = SimpleFactory.createIpad(2);
//            iphone =  SimpleFactory.createIphone(2);
//            System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc());
    }
}

工厂模式的设计有何问题?

客户端想只买一家公司的产品,客户端需要了解每种类型是出自哪一家公司?
港版ipad与iphone是出自同一家公司,美版ipad与iphone也是出自同一样公司,对于采购时

它只要报一下要什么哪个品牌的就可以买到同一个品牌的ipad和iphone


应用抽象工厂模式的设计


public interface AppleFactory {
    
    Ipad createIpad();
    
    Iphone createIphone();
}
public class HongKongCompanyAppleFactory implements AppleFactory {
    @Override
    public Ipad createIpad() {
        return new HongKongIpad();
    }

    @Override
    public Iphone createIphone() {
        return new HongKongIphone();
    }
}
public class USACompanyAppleFactory implements AppleFactory {
    @Override
    public Ipad createIpad() {
        return new USAIpad();
    }

    @Override
    public Iphone createIphone() {
        return new USAIphone();
    }
}
public class ModeClient {
    public static void main(String[] args) {
        AppleFactory appleFactory = new HongKongCompanyAppleFactory();
        Ipad ipad = appleFactory.createIpad();
        Iphone iphone = appleFactory.createIphone();
        System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc());
//        appleFactory = new USACompanyAppleFactory();
//        ipad = appleFactory.createIpad();
//        iphone = appleFactory.createIphone();
//        System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc());
     }
}

优点

缺点

深度思考
spring容器是最大的工厂
BeanFactory.getBean("xxx")
FileSystemXmlApplicationContext/ClassPathXmlApplicationContext

下面吐槽一下郭大神
考虑一下郭大神的家谱
...
郭大神的祖父
|
郭大神的父亲
|
郭大神
|
郭大神的儿子
...
郭大神的祖父创造了郭大神的父亲,郭大神的父新创造了郭大神,郭大神创造了郭大神的儿子
盘哥这边呢
...
盘哥的祖父
|
盘哥的父亲
|
盘哥
|
盘哥的儿子
...
用类来表示
interface Grandfather{
    Father createFather();
}
interface Father{
    Self createSelf();
}
interface Self {
    Son createSon();
}

如果我把郭明先生的祖父换成了姓郑(盘哥姓郑),则
当我换掉Grandfather时,奇迹出现了,整个家族的姓都变了。
从上面来看其实是工厂模式与工厂模式组合使用,经过组合后我们能写出具备极其扩展性的代码。
上面的原理是把工厂抽象成是一个产品(就是把工厂也当成是产品,那么生产这个产品的就是工厂的工厂。),于是就出现了"工厂的工厂的工厂"生产出"工厂的工厂","工厂的工厂"生产"工厂",最后工厂生成产品。这样就是级联使用工厂。
我们可以在任意一个环节替换实现,于是可以产生一个从大到小的扩展点。

考虑一下我们的建站平台框架的设计
站点有站点的接口, 同时还有站点资源接口(静态资源:js,css), 站点渲染接口, 站点数据接口。站点数据又可以分为站点数据读取接口, 和站点数据变更接口。
如果我设计下面这样的一个接口
interface SiteAPIFactory
{
    getSiteAPI();
}
interface SiteAPI{
  getSiteResourceAPI();
  getSiteDataAPI();
  getRenderAPI();
}
interface SiteDataAPI{
  getSiteDataReadAPI();
    getSiteDataDesignAPI();
}
interface SiteDataReadAPI{
    getSiteData();
}
interface SiteDataDesignAPI{
    updateSiteData();
    deleteSiteData();
}

经过上面这样设计之后, 如果我替换了SiteAPIFactory的实现, 我就可以操控整个站点的所有接口实现, 当我只替换SiteDataAPI时我能控制站点读取与写入数据的实现。也就是说从大到小的粒点扩展点我都支持了。做为平台的设计, 应用就可以根据需要自行替换实现。从而达到各种粒度的扩展。
很多模式都能组合起来使用,同时许多模式能与自身组合使用,像工厂,桥接等等
同时如果我写个SiteAPIUtil的获取SiteAPIFacotry,于是我就能提供所有接口了

SiteAPIUtil{
  SiteAPIFactory getSiteAPIFactory(){
      DefaultSiteAPIFactory.instance
  };
    SiteAPI getSiteAPI(){
        getSiteAPIFactory().getSiteAPI();
    };
    SiteDataAPI getSiteDataAPI(){
        getSiteAPI().getSiteDataAPI();
    };
    SiteDataDesignAPI getSiteDataDesignAPI(){
        getSiteDataAPI().getSiteDesignAPI();
    }
    ...
}

再认真思考一下,你会发现SiteAPIUtil其实也是一个简单工厂,可以命名为SiteAPISimpleFactory

4.模板方法

考察一下郭大神与盘哥是如何上班的
不用模式的设计
public interface Person {
    String getName();
    void summary();
}

public class GuoPerson implements Person {
    @Override
    public void summary() {
        System.out.println(getName() + "起床");
        System.out.println(getName() + "坐公交去上班");
        System.out.println(getName() + "上班到9点时上了一次厕所");
        System.out.println(getName() + "上班到10点时上了一次厕所");
        System.out.println(getName() + "上班到11点时上了一次厕所");
        System.out.println(getName() + "坐公交回到家");
        System.out.println(getName() + "洗个澡睡觉了");
    }

    @Override
    public String getName() {
        return "郭大神";
    }
}

public class ShenPerson implements Person {
    @Override
    public void summary() {
        System.out.println(getName() + "起床");
        System.out.println(getName() + "骑自行车去上班");
        System.out.println(getName() + "上班到10点时上了一次厕所");
        System.out.println(getName() + "骑自行车回到家");
        System.out.println(getName() + "洗个澡睡觉了");
    }

    @Override
    public String getName() {
        return "盘哥";
    }
}

public class Client {
    public static void main(String[] args) {
        Person person = new GuoPerson();
        person.summary();
        person = new ShenPerson();
        person.summary();
    }
}

不用模式有何问题?
他们两个上班过程基本上是一样的, 出现了重复的代码。

再考察一个人得同样要写很多重复的代码。
应用模式的设计
abstract public class AbstractPerson implements Person {

    public void summary() {
        System.out.println(getName() + "起床");
        goToCompany();
        work();
        backToHome();
        System.out.println(getName() + "洗个澡睡觉了");
    }
    abstract public void goToCompany();
    abstract public void work();
    abstract public void backToHome();
}
public class GuoPerson extends AbstractPerson {
    @Override
    public String getName() {
        return "郭大神";
    }

    @Override
    public void goToCompany() {
        System.out.println(getName() + "坐公交去上班");
    }

    @Override
    public void work() {
        System.out.println(getName() + "上班到9点时上了一次厕所");
        System.out.println(getName() + "上班到10点时上了一次厕所");
        System.out.println(getName() + "上班到11点时上了一次厕所");
    }

    @Override
    public void backToHome() {
        System.out.println(getName() + "坐公交回到家");
    }
}
public class ShenPerson extends AbstractPerson {
    @Override
    public String getName() {
        return "盘哥";
    }

    @Override
    public void goToCompany() {
        System.out.println(getName() + "骑自行车去上班");
    }

    @Override
    public void work() {
        System.out.println(getName() + "上班到10点时上了一次厕所");
    }

    @Override
    public void backToHome() {
        System.out.println(getName() + "骑自行车回到家");
    }
}
public class ModelClient {
    public static void main(String[] args) {
        Person person = new GuoPerson();
        person.summary();
        person = new ShenPerson();
        person.summary();
    }
}

优点
没有重复的代码了,新增一个人时也同样减少一直重复的代码
如果在上班完加一个先吃个饭再回家,则只需要在抽象类里加上这行代码,无需改动所有Person
缺点
多出一个类,有时多出好几个方法
深度思考
模式方法模式其实就是一个抽象的过程,是最小抽象,位于抽象的最低层
与工厂方法的区别



总结:

简单工厂能把具体实现包装起来,让客户端真正达到面向接口编程

工厂方法可以在高层进行编码,让服务端的产品线真正达到面向接口编程

抽象工厂能聚合整个产品簇,让整个服务端的多个产品线真正达到面向接口编程

模板方法同样是在高层进行编码,也同样是面向接口编程。

但工厂方法及抽象工厂方法着重抽象的是产品,而模板方法着重抽象的是步骤。

而我们通常会两者一起结合起来使用。



思考上面那个模板模式,你会发现去上班和回到家代码很相似,

于是我们组合使用工厂模式又能去除重复代码。

在抽象类返回一个交通工具,上下班和回到家就可以在基类编程了。

代码如下:

public interface Vehicle {
    String by();
}

public class Bus implements Vehicle {
    @Override
    public String by() {
        return "坐公交车";
    }
}

public class Bike implements Vehicle {
    @Override
    public String by() {
        return "骑自行车";
    }
}

abstract public class AbstractPerson implements Person {
    public void summary() {
        Vehicle vehicle = getVehicle();
        System.out.println(getName() + "起床");
        System.out.println(getName() + vehicle.by() + "去上班");
        work();
        System.out.println(getName() + vehicle.by() + "回到家");
        System.out.println(getName() + "洗个澡睡觉了");
    }
    abstract public Vehicle getVehicle();
    abstract public void work();
}

public class GuoPerson extends AbstractPerson {
    @Override
    public String getName() {
        return "郭大神";
    }

    @Override
    public void work() {
        System.out.println(getName() + "上班到9点时上了一次厕所");
        System.out.println(getName() + "上班到10点时上了一次厕所");
        System.out.println(getName() + "上班到11点时上了一次厕所");
    }

    @Override
    public Vehicle getVehicle() {
        return new Bus();
    }
}

public class ShenPerson extends AbstractPerson {
    @Override
    public String getName() {
        return "盘哥";
    }

    @Override
    public void work() {
        System.out.println(getName() + "上班到10点时上了一次厕所");
    }

    @Override
    public Vehicle getVehicle() {
        return new Bike();
    }
}

这样,在AbstractPerson的子类只需要返回交通工具, 就可以少掉两个方法

猜你喜欢

转载自stan001140.iteye.com/blog/1737817