第二篇 单例模式中枚举模式写法与简单工厂模式的组合运用

单例模式中枚举模式写法与简单工厂模式的组合运用

此处以书店系统根据客户订购的书籍种类,生成相应订单的需求,来进行一个简单模块的实现。
首先,我们需要一个书籍的基类来完成不同种类书籍的泛化。
/**
 * 书籍类的抽象父类,拥有书籍的基本属性和方法
 * 在一个图书订购系统中,书籍这一类的类会被反复实例化,因此需要使用到工厂模式
 * */
public abstract class Book {
    public String bookName;
    public String material; //材质
    public String specs; //规格
    public String bindingStyle; //装订方式

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getMaterial() {
        return material;
    }

    public void setMaterial(String material) {
        this.material = material;
    }

    public String getSpecs() {
        return specs;
    }

    public void setSpecs(String specs) {
        this.specs = specs;
    }

    public String getBindingStyle() {
        return bindingStyle;
    }

    public void setBindingStyle(String bindingStyle) {
        this.bindingStyle = bindingStyle;
    }

    public abstract void printType(); //测试用的扩展抽象方法
}
书籍这一类对象分类可从材质,体裁等方面进行分类,此处我以体裁分类进行说明。
书籍的体裁有多种,同时每一种体裁的书籍,其发行的实体也不止一本。
因此,书籍类在系统中满足了工厂模式使用的条件,也就是同一个类或同一个基类其泛型的多种类在系统中被大量独立使用的情况。
大量独立使用,也就是说,每一个类的实体对象都是需要独立存在的个体。
如果是一个个体可以被反复使用,那就不是满足工厂模式,而是应该使用单例模式。
假设:当前有诗歌、小说、散文三种文学体裁的书籍供应,而这三种体裁的书籍,都有其独立特殊的需求,那就必须有三个书籍类的泛型:
/**
 * 小说类书籍
 * */
public class NovelBook extends Book {
    @Override
    public void printType() {
        setFitPeople();
    }

    /*独立的需求:显示适龄读者*/
    public void setFitPeople(){
        System.out.println("该小说适合青少年阅读!");
    }
}
/**
 * 诗歌类书籍
 * */
public class PoetryBook extends Book{
    @Override
    public void printType() {
        setPublishCountry();
    }

    /*独立的需求:显示发行国家*/
    public void setPublishCountry(){
        System.out.println("该诗歌集在全球范围发行!");
    }
}
/**
 * 散文类书籍
 * */
public class ProseBook extends Book {
    @Override
    public void printType() {
        setEmotion();
    }

    /*独立的需求:显示体裁优势*/
    public void setEmotion(){
        System.out.println("散文是作者情感抒发最好的文学形式载体!");
    }
}
而书籍类工厂,则如上面所说,属于能够被反复使用的类,满足了单例设计模式的条件。
此处,我以消耗最小,线程安全的单例枚举模式做为单例模式使用的例子:
/*书籍工厂*/
public enum  BookFactory {
    BOOK_FACTORY;
    public Book createBook(String bookType){
        Book book = null;
        if("poetry" == bookType){
            //诗歌
            book = new PoetryBook();
        }
        else if("novel" == bookType){
            //小说
            book = new NovelBook();
        }
        else if("prose" == bookType){
            //散文
            book = new ProseBook();
        }

        if(book != null)
            book.printType();

        return book;
    }
}
此处为便于说明,以if…else…分支的形式来进行工厂类生成实例的编写。有兴趣使用反射配合工厂模式实现类实例的朋友,可以自行改进。

通过上面的代码,我们初步完成了书籍类的编码。而在订单系统中,订单同样属于大量独立使用的情况。
而一个书店的经营范围也不仅仅是书籍,因此订单就有可能存在书籍类订单、笔类订单等。
因而,我们便需要一个订单的基类,多种订单基类的泛型,以及一个订单工厂,如下:
/**
 * 订单的抽象类
 * */
public abstract class Order {
    // 假如存在线上线下购买途径时,还要区分线上订单还是线下订单
    public String orderWay; 

    public String getOrderWay() {
        return orderWay;
    }

    public void setOrderWay(String orderWay) {
        this.orderWay = orderWay;
    }
}
/**
 * 书籍订单类
 * */
public class BookOrder extends Order{
    private String client;
    private Book orderBook;

    public String getClient() {
        return client;
    }

    public void setClient(String client) {
        this.client = client;
    }

    public Book getOrderBook() {
        return orderBook;
    }

    public void setOrderBook(Book orderBook) {
        this.orderBook = orderBook;
    }
}
/*笔类订单*/
public class PenOrder extends Order{


}
/*订单工厂*/
public enum  OrderFactory {
    ORDER_FACTORY;
    public Order creatOrder(String orderWay, String itemType, Book book){
        Order order = null;
        if("book" == itemType){
            order = new BookOrder();
            order.setOrderWay(orderWay);
            ((BookOrder) order).setOrderBook(book);
        }
        else if("pen" == itemType){
            order = new PenOrder();
        }
        else {
            System.out.println("目前只支持书籍和笔订购!");
        }
        return order;
    }
}
由于订单与书籍在设计时的思路相似,此处不再累述。

在上层的书店,此处不再做商城化假设。只假定只有一家,因此书店在系统中,也可以使用单例模式:
public enum  BookStore {
    BOOK_STORE;
    public static Order creatOrder(String orderWay, String itemType, String bookType){
        Order order = OrderFactory.ORDER_FACTORY.creatOrder(
            orderWay, // 线上还是线下订单
            itemType, // book or pen
            BookFactory.BOOK_FACTORY.createBook(bookType) // 遵循迪米特法则,尽量不要在局部变量中出现陌生类
        );
        return order;
    }
}

在模拟客户端,进行书籍的下单操作:
/*购买书籍的入口*/
public class BuyBook  {
    public static void main(String[] args) {
        Order order1 = BookStore.creatOrder("onLine","book","poetry");
        Order order2 =BookStore.creatOrder("offLine","book","novel");
    }
}
最终执行时,会调用各个书籍类子类实现的基类的printType方法,打印如下内容:
该诗歌集在全球范围发行!
该小说适合青少年阅读!

到此,我们便实现了一个单例模式枚举法与简单工厂模式的简单组合运用。
顺便一提,简单工厂模式常用,但又不属于23种设计模式之一。
大多数朋友的争论点在于简单工厂模式违背了设计模式七大原则的开闭原则(Open Close Principle OCP)
其主要原因是指在简单工厂模式中,使用if…else…分支判断创建实体对象时,如果实体对象的种类增加后,需要修改工厂类的代码才能实现适用需求变化。
在上面举得例子中,就是比如新增了一种体裁的书籍,那就要去修改代码才能实现。
而开闭原则,其中最重要的一点就是,对实体提供方进行扩展,而不影响实体的使用方。
严格来说,扩展的意思应该符合里氏替换原则(Liskov Substitution Principle LSP)
那对代码的扩展应该通过继承方式,而不是通过修改方法内部代码方式实现。
因而,在这一点上,我认为简单工厂模式确实违背了开闭原则。
但当使用反射代替分支时,似乎符合了开闭原则。不过,另一方面却增加了性能开销。实际使用时,就要视具体情况而论了。

笔者话:知识的理解深度,随着每一位朋友对知识的运用而有所不同。达者为师!若在下所说内容中有缺漏或错误,还望指正!由衷感谢!

互促互进,常学常新!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值