设计模式1:单例模式、工厂、创建者、原型

设计模式,一种抽象、总结

Gang of Four(GOF)分类:3大类23种;

创建型模式、结构型模式和行为型模式

几个设计原则:

  1. 接口分离(依赖倒置)原则:编程面向接口而不是实现
  2. 单一原则:单一部分完成特定的分类功能、封装
  3. 开闭原则:对修改是关闭的、对拓展是开发的
  4. 里氏代换原则:子类对象可以出现在任何父类对象的使用下
  5. 迪米特?

第一类:创建型模式

创建型模式的作用就是创建对象,提供不同需求的创建形式

1. 单例模式 Singleton

单例模式:一个类,我们只能生成该类的一个对象。单例模式用得最多,但容易理解出错。

单例模式一般又分为饿汉式、懒汉式和内嵌式三种(其实《剑指offer》一书中第二个题就是这个)。

不论哪种形式的单例模式都需要屏蔽掉new一个对象的关键词,所以其构造函数必须使用private修饰;其次,通过一个静态方法来完成实例的返回。

  • 饿汉式:无论如何都会生成一个实例,并且在多线程下是不合理的

    public class Singleton {
        // 首先,将 new Singleton() 堵死
        private Singleton() {};
        // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
        private static Singleton instance = new Singleton(); 
        public static Singleton getInstance() {
            return instance;
        }
        // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...),
        // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了
        public static Date getDate(String mode) {return new Date();}
    }
    
  • 懒汉式:使用同步锁机制来完成,能够解决多线程下的一个实例问题;懒汉式不用先实例化对象,并且在加锁前后做两次判断

    public class Singleton {
        private Singleton() {}
        // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
        private static volatile Singleton instance = null; 
        public static Singleton getInstance() {
            if (instance == null) {
                // 加锁
                synchronized (Singleton.class) {
                    // 这一次判断也是必须的,不然会有并发问题
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
  • 内嵌式:在实际使用时才会去实例化对象。内部类是私有的,其他人无法直接调用;只要不调用Singletion3.getInstance,就不会创建实例。

    public class Singleton3 { 
        private Singleton3() {}
        // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法的特性
        private static class Holder {
            private static Singleton3 instance = new Singleton3();
        }
        public static Singleton3 getInstance() {
            return Holder.instance;
        }
    }
    

 

2. 简单工厂

很简单,就是一个工厂,生成一类实例对象;一般,根据不同的类型参数,生产出不同的对象返回。这里强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

public class FoodFactory { 
    public static Food makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

简单工厂会受到不同的类型参数的限制,如果需要不同的或者更多的类型,就需要对工厂代码进行修改。这将违背开闭原则。

另一种形式的简单工厂,将不同的类型实行放到若干不同的方法中;相当于改变了if-else或者switch的格局

1551102359840

与工厂模式而言,简单工厂更像一种习惯,不能那么准确的说成为一种设计模式。一般简单工厂的方法都是静态的,不需要实例化工厂对象,也称静态工厂。

 

3. 工厂模式

工厂模式可以看成是多种不同业务的工厂,不同工厂各司其职,职责单一;使用中我们根据不同参数,先选择具象的工厂,反观其内部做的都是生成对象实例的活(生产方法一样);

1551102191958

其实,上述的图只是一种表现形式。按笔者的理解,工厂模式应该是又将new创建实例的过程放回到创建者中,只是把这个问题放大了,我们可能需要许多不同类型的工厂,来生产不同类型的对象。

工厂模式的核心是工厂方法的实现,交给了具体的子类来完成。在一些例子中,工厂模式通常将通用抽取出一个抽象类或者接口,来声明工厂方法和一些其他的处理;而把具体的实现交给具体的子类去实现不同的工厂方法。使用是依然用该类的对象去动态多态的创建不同的子工厂对象。

1551143622579

工厂模式中,创建者抽象和具体创建者的分离,是能够有效改变了对于具体的产品对象,工厂不再依赖不同的产品实例;反之,具体的产品实例需要依赖抽象的产品,而抽象的创建者也只依赖抽象的产品——DIP:依赖倒置原则

 

4. 抽象工厂

抽象工厂更多时候,面向的是一类产品族的概念。

一个产品有多种不同类型,就需要一个工厂模式(一个工厂方法,N个具体实现)来创建不同的具象产品。

一个产品族就需要M个这样的工厂,他们之间可能还存在某种集合关系。

一个例子:

1551145097869

抽象工厂,某种程度上将是会违背开闭原则的。如果需要一类新的产品族,则需要修改所有的工厂。下例中A、B分别表示一类产品族。

1551144578947

 

5. 创建者模式

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,
所谓复合对象就是指某个类具有不同的属性。

public class Builder {
 	private List<Sender> list = new ArrayList<Sender>(); 
  	public void produceMailSender(int count) {
 		for (int i = 0; i < count; i++) {
 			list.add(new MailSender());
  		}
 	}
 
    public void produceSmsSender(int count) {
      	for (int i = 0; i < count; i++) {
      		list.add(new SmsSender());
     	}
    }
 }

更直观而言,创建者模式XXBuilder,应该是一条链式的创建者反应。

Food food = new FoodBuilder().a().b().c().build();
Food food = Food.builder().a().b().c().build();

一个标准案例如下:

class User {
    // 下面是“一堆”的属性
    private String name;
    private String password;
    private int age; 
    // 构造方法私有化,不然客户端就会直接调用构造方法了
    private User(String name, String password, int age) {
        this.name = name;  this.password = password;     this.age = age;
    }
    // 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
    // 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好
    public static UserBuilder builder() {
        return new UserBuilder();
    } 
    public static class UserBuilder {
        // 下面是和 User 一模一样的一堆属性
        private String  name;
        private String password;
        private int age; 
        
        private UserBuilder() {     } 
        // 链式调用设置各个属性值,返回 this,即 UserBuilder
        public UserBuilder name(String name) {
            this.name = name;
            return this;
        } 
        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }
        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }
 
        // build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。
        // 当然,可以在 “复制” 之前做点检验
        public User build() {
            if (name == null || password == null) {
                throw new RuntimeException("用户名和密码必填");
            }
            if (age <= 0 || age >= 150) {
                throw new RuntimeException("年龄不合法");
            }
            // 还可以做赋予”默认值“的功能
            return new User(name, password, nickName, age);
        }
    }
}
---------------------------------------------------
public class APP {
    public static void main(String[] args) {
        User d = User.builder()
                .name("foo")
                .password("pAss12345")
                .age(25)
                .build();
    }
}

创建者模式看上去十分冗余,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 return this 就可以了,然后就可以像下面这样调用:

User user = new User().setName("").setPassword("").setAge(20);

 

6. 原型模式

原型模式很简单:有一个原型实例,基于这个原型实例产生新的实例,也就是“克隆”了。

Object 类中有一个 clone() 方法,它用于生成一个新的对象,当然,如果我们要调用这个方法,java 要求我们的类必须先实现 Cloneable 接口,此接口没有定义任何方法,但是不这么做的话,在 clone() 的时候,会抛出 CloneNotSupportedException 异常。

protected native Object clone() throws CloneNotSupportedException;

java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。

7. 创建型模式总结

创建型模式总体上比较简单,它们的作用就是为了产生实例对象,算是各种工作的第一步了,因为我们写的是面向对象的代码,所以我们第一步当然是需要创建一个对象了。

简单工厂模式最简单;工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式。单例模式就不说了,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;建造者模式专门对付属性很多的那种类,为了让代码更优美;原型模式用得最少,了解和 Object 类中的 clone() 方法相关的知识即可

 

【搬运博客】:https://javadoop.com/post/design-pattern

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值