设计模式之工厂模式(简单工厂,工厂方法,抽象工厂)

670 阅读6分钟

注意:设计模式一般都输混用的

简单工厂模式

指由一个工厂对象决定创建出哪一种产品类的实例, 但它不属于 GOF 23 种设计模式。简单工厂适用于工厂类负责创建的对象较少的场景,且客户端只需要 传入工厂类的参数,对于如何创建对象的逻辑不需要关心。

Demo
以支付为例,当然这里的场景不够完美,真实的支付很复杂,平台上有各种支付,比如支付宝支付,微信支付,银联支付

// 抽象的支付方式
public interface PayMethod {
    // 直接调用支付
    public void playPay();
}
// 这里的抽象类是为了定义一些抽象方法和模板
public abstract class AbstractPayMethod implements PayMethod {

    // 1. 验证订单
    public abstract void validationOrder();
    // 2. 验证账户余额
    public abstract void validationAccount();
    // 3.支付
    public abstract void pay();
    // 4.通知
    public abstract void notice();


    public void playPay() {
        validationOrder();
        validationAccount();
        pay();
        notice();
    }

}
//支付宝支付
public class AliPay extends AbstractPayMethod {
    @Override
    public void validationOrder() {
        System.out.println("支付宝支付验证订单");
    }

    @Override
    public void validationAccount() {
        System.out.println("支付宝验证账户");
    }

    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }

    @Override
    public void notice() {
        System.out.println("通知");
    }
}
// 微信支付
public class WeChatPay extends AbstractPayMethod {
    @Override
    public void validationOrder() {

    }

    @Override
    public void validationAccount() {

    }

    @Override
    public void pay() {

    }

    @Override
    public void notice() {

    }
}

// 工厂类
public class PayFactory {

    private PayFactory(){}

    private final static Map<Class<? extends PayMethod>,PayMethod> payMap = new HashMap<>();

    // 如果我想修改或者新增支付方式要修改这个工厂类 不满足开闭原则
    static {
        payMap.put(AliPay.class,new AliPay());
        payMap.put(WeChatPay.class,new WeChatPay());
        // 银联支付 自己写
        payMap.put(UnionPay.class,new UnionPay());
        // 跨境支付
        payMap.put(CrossBorderPay.class,new CrossBorderPay());
    }

    public static PayMethod createPayMethod(Class<? extends PayMethod> clazz) {
        return payMap.get(clazz);
    }

}


//测试类
public class MainTest {

    public static void main(String[] args) {
        PayMethod payMethod = PayFactory.createPayMethod(AliPay.class);
        payMethod.playPay();
    }
}

工厂类只负责生成对象,此模式不符合开闭原则,如果要想新加产品,要修改工厂类,此模式只能符合生成小部分对象,如果太多了,就成了万能工厂,不好维 护。

JDK中的运用

Calendar这个类的getInstance方法.


private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        //这里就用了简单工厂
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

工厂方法模式

是指定义一个创建对象的接口,但让实现这个接口的类 来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。在工厂方法模式中用户只需要关心所 需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则。

工厂方法模式主要解决产品扩展的问题,在简单工厂中,随着产品链的丰富,如果每个对象的创建 逻辑有区别的话,工厂的职责会变得越来越多,有点像万能工厂,并不便于维护。根据单一职责原则我 们将职能继续拆分,专人干专事。

Demo

// 支付方式的抽象
public interface PayMethodInterface {
    // 调用支付
    public void playPay();
}
public abstract class AbstractPayMethod implements PayMethodInterface{
    // 1. 下单请求
    public abstract void placeOrder();
    // 2.支付
    public abstract void pay();
    // 3.通知
    public abstract void notice();

    public void playPay() {
        placeOrder();
        pay();
        notice();
    }
}

// 抽象的工厂
public abstract class PayFactory {
    public abstract PayMethodInterface createPayMethod();
}

// 支付宝
public class AliPay extends AbstractPayMethod {

    @Override
    public void placeOrder() {
        System.out.println("支付宝下单请求");
    }

    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }

    @Override
    public void notice() {
        System.out.println("支付宝通知");
    }
}

//微信
public class WeChatPay extends AbstractPayMethod {

    @Override
    public void placeOrder() {
        System.out.println("微信支付下单请求");
    }

    @Override
    public void pay() {
        System.out.println("微信支付");
    }

    @Override
    public void notice() {
        System.out.println("通知");
    }
}
// 阿里的工厂
public class AliPayFactory extends PayFactory {
    @Override
    public PayMethodInterface createPayMethod() {
        return new AliPay();
    }
}


// 微信的工厂
public class WeChatPayFactory extends PayFactory{
    @Override
    public PayMethodInterface createPayMethod() {
        return new WeChatPay();
    }
}

// 测试类
public class MainTest {
    public static void main(String[] args) {
        PayFactory payFactory = new AliPayFactory();
        PayMethodInterface payMethod = payFactory.createPayMethod();
        payMethod.playPay();
    }
}

JDK中的运用

每一个工厂产生一个Logger.

工厂方法适用于以下场景:
1、创建对象需要大量重复的代码。
2、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。
3、一个类通过其子类来指定创建哪个对象。
工厂方法也有缺点:
1、类的个数容易过多,增加复杂度。
2、增加了系统的抽象性和理解难度。

抽象工厂模式

指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。客户端(应用层)不依赖于产品类实例如何被创建、实现等细节,强调的是一 系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。需要提供一个产品类 的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

从上图中看出有正方形,圆形和菱形三种图形,相同颜色深浅的就代表同一个产品族,相同形状的代表 同一个产品等级结构。同样可以从生活中来举例,比如,美的电器生产多种家用电器。那么上图中,颜 色最深的正方形就代表美的洗衣机、颜色最深的圆形代表美的空调、颜色最深的菱形代表美的热水器, 颜色最深的一排都属于美的品牌,都是美的电器这个产品族。再看最右侧的菱形,颜色最深的我们指定 了代表美的热水器,那么第二排颜色稍微浅一点的菱形,代表海信的热水器。同理,同一产品结构下还 有格力热水器,格力空调,格力洗衣机。

Demo

// 支付方式抽象
public interface PayMethodInterface {
    // 支付
    public IPay playPay();

    // 结算

    // 退款
    public IRefund refund();
    //订单状态查询

    // 支付方式(快捷支付,协议支付)

    //跨境支付(支持货币,汇率换算)
}

// 支付
public interface IPay {
    // 1. 下单请求
    public abstract void placeOrder();
    // 2.支付
    public abstract void pay();
    // 3.通知
    public abstract void notice();
}

//退款
public interface IRefund {
    // 发起退款请求
    public void refund();

    // 验证订单
    public void validationOrder();
}

// 支付宝支付
public class AliPay implements IPay {
    @Override
    public void placeOrder() {
        System.out.println("支付宝支付下单请求");
    }

    @Override
    public void pay() {
        System.out.println("支付宝支付");
    }

    @Override
    public void notice() {
        System.out.println("支付宝支付通知");
    }
}
// 支付宝退款
public class AliRefund implements IRefund {
    @Override
    public void refund() {
        System.out.println("支付宝退款请求");
    }

    @Override
    public void validationOrder() {
        System.out.println("支付宝验证订单");
    }
}

// 支付宝的工厂
public class AliPayFactory implements PayMethodInterface {

    @Override
    public IPay playPay() {
        return new AliPay();
    }

    @Override
    public IRefund refund() {
        return new AliRefund();
    }
}

// 微信支付
public class WeChatPay implements IPay {
    @Override
    public void placeOrder() {
        System.out.println("微信下单请求");
    }

    @Override
    public void pay() {
        System.out.println("微信支付");
    }

    @Override
    public void notice() {
        System.out.println("微信通知");
    }
}
// 微信退款
public class WeChatRefund implements IRefund {
    @Override
    public void refund() {
        System.out.println("微信退款请求");
    }

    @Override
    public void validationOrder() {
        System.out.println("微信验证订单");
    }
}

// 微信工厂
public class WeChatFactory implements PayMethodInterface {
    @Override
    public IPay playPay() {
        return new WeChatPay();
    }

    @Override
    public IRefund refund() {
        return new WeChatRefund();
    }
}


其实这个也是不符合开闭原则的,因为增加产品会修改抽象工厂,然后每个子类的都需要去修改。