经典伴读_GOF设计模式_创建型模式

经典伴读系列文章,不是读书笔记,自己的理解加上实际项目中运用,旨在5天读懂这本书。如果这篇文章对您有些用处,请点赞告诉我O(∩_∩)O。
在这里插入图片描述

为什么要读这本

《设计模式:可复用面向对象软件的基础》这本书对于我来说很难看,原因:

  • 重思想轻代码,意味着代码大多不完整,甚至只有类图。
  • 使用C++,Smalltalk,我是Javaer
  • 翻译生涩

即使如此,为什么还要读下去?
因为设计模式从本书开始,可以了解初衷,其他的书都有可能过分解读。四个作者又称为四人组(Gang Of Four),因此这本书又叫做《GOF设计模式》。

创建型模式

GOF中23种设计模式从用途上分为三类,第一类是创建型模式,它抽象了实例化过程:对象的创建,初始化以及组织。

Singleton单例

保证一个类只有一个实例,并提供一个全局访问点。

单例实际项目中用到最多,有很多种实现,这里介绍两种:
(GOF中没有提到饿汉,懒汉)
1、只需要一个单例类时
可以在类加载时初始化唯一的实例。

public class Singleton {
    private static final Singleton SINGLETON = new Singleton();
    private Singleton() {}
    public Singleton getInstance() {
        return SINGLETON;
    }
}

2、需要多个单例类时
如果数十个类都需要单例,在每个类中都加入上面4行代码,这个显然不那么美好,可以使用单例注册表方式实现。

public class SingletonRegister {
    private final static Map SINGLETON_CACHE=new HashMap();
    public static <T> T getInstance(Class<T> clazz) {
        T t = (T) SINGLETON_CACHE.get(clazz.getName());
        //从cache中能获取到已经初始好的实例返回,如果没有则需要创建
        if (t == null) {
            //为了保证只有一个实例生成,需要同步控制,即同时只有一个线程执行创建过程
            synchronized (SINGLETON_CACHE) {
                t = (T) SINGLETON_CACHE.get(clazz.getName());
                if (t == null) {
                    try {
                        t = clazz.newInstance();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    SINGLETON_CACHE.put(clazz.getName(), t);
                }
            }
        }
        return t;
    }
    ......

Factory Method工厂方法

1、GOF中有两种工厂模式,一种工厂方法,一种抽象工厂。其他文章中还有提到简单工厂,无论哪种工厂,我们得先了解什么是工厂?

	interface Product {} //产品

    public static class ProdcutA implements Product {}

    public static class Factory { //工厂
        public Product create() {
            return new ProdcutA();
        }
    }

    public static void main(String[] args) {
    	//调用方不依赖具体产品
        Factory factory = new Factory();
        Product product = factory.create();
        System.out.println(product);
    }

代码简单,思想不简单,为什么不直接new ProductA()?因为要依赖抽象而不是依赖实现。
Factory就是工厂,它封装了具体产品的创建(create方法),调用方知道获取的是抽象产品Prodcut,却不知道具体是哪一种产品。后面如果PD说我们要换一种产品ProductB生产,你会感谢这种设计,去掉了批量替换或者升级jar包的风险。

2、上面的工厂已经实现依赖倒置,如果要同时生产实现Prodcut的多种商品,如:ProdcutA,ProductB,该怎么办?
可以在create方法中,加入参数type,根据不同类型创建多种产品。这种方式被称为简单工厂模式。(GOF称为参数化工厂方法,实际并不符合工厂方法模式的定义)

    interface Product{}
    public static class ProductA implements Product {}
    public static class ProductB implements Product {}
    
	public static class Factory {
        public static Product create(int type) {
            Product product;
            switch (type) {
                case 1:
                    product = new ProductA();
                    break;
                case 2:
                    product = new ProductB();
                    break;
                default:
                    throw new RuntimeException("没有找到类型为" +type+"的产品");
            }
            return product;
        }
    }

    public static void main(String[] args) {
        //调用方不感知具体产品
        Product a = Factory.create(1);
        System.out.println(a);
        Product b = Factory.create(2);
        System.out.println(b);
    }

3、也可以将工厂中创建对象方法create变为抽象方法,那么多个子类实现就可以创建多种产品。这里的抽象create方法就是工厂方法,这种方式就称为工厂方法模式。

定义一个用于创建对象的接口,让子类决定实例化哪一个类。 Factory Method使一个类的 实例化延迟到其子类。

在这里插入图片描述

interface Product{}
    public static class ProductA implements Product {}
    public static class ProductB implements Product {}

    interface Factory {
        Product create();
    }
    public static class FactoryA implements Factory {
        @Override
        public Product create() {
            return new ProductA();
        }
    }
    public static class FactoryB implements Factory {
        @Override
        public Product create() {
            return new ProductB();
        }
    }

    public static void main(String[] args) {
        //调用方虽然依赖工厂类,但不感知具体产品
        Factory factoryA = new FactoryA();
        Product a = factoryA.create();
        System.out.println(a);

        Factory factoryB = new FactoryB();
        Product b = factoryB.create();
        System.out.println(b);
    }

4、GOF中给出了另一种工厂方法模式的应用场景,类似于模版方法模式,区别在于前者是将生成实例交给子类,后者是将具体处理步骤交给子类。
在应用中创建多种文档,如下:在这里插入图片描述

	public static abstract class Application { //抽象工厂Factory
        private List<Document> docs = new ArrayList<>();

        public abstract Document createDocument(); //工厂方法create()
        public Document newDocument() {
            Document doc = createDocument();
            docs.add(doc);
            doc.open();
            return doc;
        }
    }

    public static class MyApplication extends Application { 
        @Override
        public Document createDocument() { 
            return new MyDocument(); //子类负责实例化具体产品
        }
    }

Abstract Factory抽象工厂

上面介绍过的简单工厂模式和工厂方法模式都可以生产多产品,但这有个前提:需要生产的商品需要实现同一个Prodcut接口,可以理解为必须为同类型商品。如衣服有多种,T恤、夹克,毛衣,短袖等。那么如果要生产的是整套的产品,如校服不仅有衣服,还有裤子,裙子。电脑不仅有主机,还有显示器,键盘。没错,这里可能不止一个产品接口。当需要创建的是整套产品时,就需要使用抽象工厂模式。

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

定义有些难懂,直接看例子,现在要为南京路小学生产校服,校服是成套生产,男生包含衬衣和裤子,女生包含衬衣和裙子。
在这里插入图片描述

	//三类产品
	interface Shirt {} //衬衣
    interface Pants {} //裤子
    interface Skirt {} //裙子

    interface BoyUniformFactory { //男生校服工厂
        Shirt createShirt();
        Pants createPants();
    }
    interface GirlUniformFactory { //女生校服工厂
        Shirt createShirt();
        Skirt createSkirt();
    }

	//衬衫有三种款式
    public static class ShirtA implements Shirt {}
    public static class ShirtB implements Shirt {}
    public static class ShirtC implements Shirt {}

    //裤子有两种款式
    public static class PantsA implements Pants {}
    public static class PantsB implements Pants {}

    //裙子有两种款式
    public static class SkirtA implements Skirt {}
    public static class SkirtB implements Skirt {}

    //南京路小学-男生校服工厂
    public static class BoyUniformFactoryForNanjingRoad implements BoyUniformFactory {
        @Override
        public Shirt createShirt() {
            return new ShirtA();
        }

        @Override
        public Pants createPants() {
            return new PantsA();
        }
    }
    //南京路小学-女生校服工厂
    public static class GirlUniformFactoryForNanjingRoad implements GirlUniformFactory {
        @Override
        public Shirt createShirt() {
            return new ShirtA();
        }

        @Override
        public Skirt createSkirt() {
            return new SkirtA();
        }
    }

    public static void createUniform(BoyUniformFactory boyUniformFactory
            , GirlUniformFactory girlUniformFactory) {
        System.out.println("---------" + boyUniformFactory + "---------");
        Shirt boyShirt = boyUniformFactory.createShirt();
        Pants pants = boyUniformFactory.createPants();
        System.out.println(boyShirt);
        System.out.println(pants);

        System.out.println("---------" + girlUniformFactory + "---------");
        Shirt girlShirt = girlUniformFactory.createShirt();
        Skirt skirt = girlUniformFactory.createSkirt();
        System.out.println(girlShirt);
        System.out.println(skirt);
    }

    public static void main(String[] args) {
        //生成南京路小学校服
        BoyUniformFactory boyUniformFactory = new BoyUniformFactoryForNanjingRoad();
        GirlUniformFactory girlUniformFactory = new GirlUniformFactoryForNanjingRoad();
        createUniform(boyUniformFactory, girlUniformFactory);
    }

GOF中的例子类似于给桌面程序换主题(外观),需要创建两个不同的主题套件(Mott,PM),每个套件中都有两种窗口组件(Window,ScrollBar)。
在这里插入图片描述

Prototype原型

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

工厂方法模式,抽象工厂模式都可以创建多种产品,但他们都需要大量的工厂类,这无疑会增加系统的复杂度。有没有办法可以既能生产多种产品,又能减少所需类的数量?原型模式是解决办法之一。我们用一个原型管理器保存可用原型注册表(map实现),事先注册好原型,调用方获取产品时,先检索注册表中的原型,再根据原型克隆出对象实例。
在这里插入图片描述

interface Product extends Cloneable {
        Product createClone() throws Exception;
    }
    public static class ProductA implements Product {
        @Override
        public Product createClone() throws Exception {
            return (Product) clone();
        }
    }
    public static class ProductB implements Product {
        @Override
        public Product createClone() throws Exception {
            return (Product) clone();
        }
    }

    public static class PrototypeManager {
        private static Map<String, Product> prototypeMap = new HashMap<>();

        public static void register(String name, Product product) {
            prototypeMap.put(name, product);
        }

        public static Product create(String name) {
            Product p = prototypeMap.get(name);
            try {
                return p.createClone(); //找不到原型报异常
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    public static void registerPrototypes() {
        PrototypeManager.register("productA", new ProductA());
        PrototypeManager.register("productB", new ProductB());
    }

    public static void main(String[] args) {
        //服务方注册原型
        registerPrototypes();

        //调用方创建产品实例(无感知具体产品类)
        Product productA = PrototypeManager.create("productA");
        System.out.println(productA);
        Product productB = PrototypeManager.create("productB");
        System.out.println(productB);
    }

GOF中甚至提出,不仅工厂类可以省掉,如果使用通用产品实现类,在注册时区别定制原型,这样连产品类都可以省掉。

Builder生成器

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

构建一份文档数据,也可以有多种表示方式,如HTML,TXT,图片,PDF等。类似这种统一构建多种输出的场景可以使用Builder生成器模式。
在这里插入图片描述

interface Builder {
        //构建各个部分buildPart
        void buildHead(String title);
        void buildBody(String[] contents);
        void buildFoot(String foot);
		//获取构建结果
        String getResult(); 
    }

    public static class TXTBuilder implements Builder{
        private StringBuilder builder = new StringBuilder();

        @Override
        public void buildHead(String title) {
            builder.append("title:" + title + "\n");
            builder.append("----------------------------\n");
        }

        @Override
        public void buildBody(String[] contents) {
            builder.append("contents:\n");
            for (String content : contents) {
                builder.append(content + "\n");
            }
            builder.append("----------------------------\n");
        }

        @Override
        public void buildFoot(String foot) {
            builder.append("foot:" + foot + "\n");
        }

        @Override
        public String getResult() {
            return builder.toString();
        }
    }

    public static class HTMLBuilder implements Builder {
        private PrintWriter writer;
        private String fileName;

        @Override
        public void buildHead(String title) {
            try{
                fileName = title + ".html";
                writer = new PrintWriter(fileName);
                writer.println("<html><head><title>" + title + "</title></head>");
                writer.println("<body><h1>" + title + "</h1>");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void buildBody(String[] contents) {
            writer.println("<ul>");
            for (String content : contents) {
                writer.println("<li>" + content + "</li>");
            }
            writer.println("</ul>");
        }

        @Override
        public void buildFoot(String foot) {
            writer.println("<p>"+ foot +"</p></body></html>");
            writer.close();
        }

        @Override
        public String getResult() {
            return fileName;
        }
    }
	//经理类确定构建步骤(先做什么,再做什么)
    public static class Director {
        private Builder builder;

        public Director(Builder builder) {
            this.builder = builder;
        }
		//使用生成器构建过程
        public void construct() {
            builder.buildHead("SalesTop");
            builder.buildBody(new String[]{"Book1", "Book2", "Book3", "Book4", "Book5"});
            builder.buildFoot("Copyright 2004-2022 flyzing");
        }
    }

    public static void buildResult(Builder builder) {
        Director director = new Director(builder);
        director.construct();
        String result = builder.getResult();
        System.out.println(result);
    }

    public static void main(String[] args) {
        buildResult(new TXTBuilder());
        buildResult(new HTMLBuilder());
    }

未完待续

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值