概述
目录
1、设计模式总览
2、创建型模式
2.1 单例模式
2.2 工厂模式
2.3 建造者模式
1、设计模式总览
2、创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
2.1 单例模式
1、简介
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中 的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化 过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
2、要点
-
某个类只能有一个实例
-
它必须自行创建这个实例
-
必须自行向整个系统提供这个实例
3、实现方式
懒汉式,顾名思义就是实例再用到的时候才去创建,比较懒,用的时候才去检查有没有实例,如果有则返回,没有则创建。有线程安全和线程不安全两种写法,区别就是synchronized关键字
懒汉式(线程不安全)
/**
* @Author:Shane
* @Date 2020/7/21 16 37
* 单例模式,线程不安全
*/
public class Singleton1 {
private static Singleton1 singleton;
private Singleton1(){
}
public static Singleton1 getInstance(){
if(singleton == null){
singleton = new Singleton1();
}
return singleton;
}
}
懒汉式(线程安全)
/**
* @Author:Shane
* @Date 2020/7/21 16 39
* 懒汉式,线程安全
*/
public class Singleton2 {
private static Singleton2 singleton;
private Singleton2(){
}
public static synchronized Singleton2 getInstance(){
if(singleton == null){
singleton = new Singleton2();
}
return singleton;
}
}
饿汉式
饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你 有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
/**
* @Author:Shane
* @Date 2020/7/21 16 44
*/
public class EHan {
private static EHan intance = new EHan();
private EHan(){}
public static EHan getInstance(){
return intance;
}
}
双检索
双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现 中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全, 又比直接上锁提高了执行效率,还节省了内存空间。
/**
* @Author:Shane
* @Date 2020/7/21 16 48
* 双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,
* 特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
*/
public class DoubleCheck {
//由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。
//同时可以让变量透明,其他线程也可以发现(内存可见性)
private static volatile DoubleCheck instance;
private DoubleCheck(){}
public static DoubleCheck getInstance(){
if(instance == null){
synchronized (DoubleCheck.class){
if(instance == null){
instance = new DoubleCheck();
}
}
}
return instance;
}
}
/**
* @Author Shane
*/
public class VolatieThread {
private static volatile boolean stop = true;
public static void main(String[] args) throws InterruptedException {
//这个线程看不到主线程的false被修改了
new Thread(new Runnable() {
@Override
public void run() {
int i = 1;
while(stop){
i++;
}
System.out.println(DoubleCheck.getInstance());
}
}).start();
Thread.sleep(1000);
stop = false;
System.out.println(DoubleCheck.getInstance());
}
}
静态内部类
静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双 检锁方式可在实例域需要延迟初始化时使用
/**
* @Author:Shane
* @Date 2020/7/21 16 50
*/
public class Singleton {
private static class SinletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SinletonHolder.INSTANCE;
}
}
枚举
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且它还自动支持序列化机制,绝对防止多次实例化
/**
* @Author:Shane
* @Date 2020/7/21 19 43
*/
public class EnumSingleton {
private EnumSingleton(){
}
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
}
private static enum Singleton{
INSTANCE;
private EnumSingleton singleton;
private Singleton(){
singleton = new EnumSingleton();
}
public EnumSingleton getInstance(){
return singleton;
}
}
}
4、拓展
Spring依赖注入Bean实例默认是单例的Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean()里。getBean的doGetBean方法调用getSingleton进行bean的创建。lazy-init方式,在容 器初始化时候进行调用,非lazy-init方式,在用户向容器第一次索要bean时进行调用
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
从上面代码可以看到,spring依赖注入时,使用了双重判断加锁的单例模式,首先从缓存中获取bean实例,如果为null,对缓存map加锁,然后再从缓存中获取bean,如果继续为null,就创建一个bean。这样双重判断,能够避免在加锁的瞬间,有其他依赖注入引发bean实例的创建,从而造成重复创建的结果。
在这里Spring并没有使用私有构造方法来创建bean,而是通过 singletonFactory.getObject()返回具体beanName对应的ObjectFactory来创建bean。
我们一路跟踪下去,发现实际上是调用了AbstractAutowireCapableBeanFactory的 doCreateBean方法,返回了BeanWrapper包装并创建的bean实例。
ObjectFactory主要检查是否有用户定义的BeanPostProcessor后处理内容,并在创建bean时进行处理,如果没有,就直接返回bean本身
2.2 工厂模式
1、工厂模式简介
工厂模式:建立创建对象的工厂在面向对象编程中,术语 “工厂” 表示一个负责创建其它类型对象的类。通常情况下, 作为一个工厂的类有一个对象以及与他关联的多个方法。客户端使用某些参数调用此 方法之后,工厂会据此创建所需类型的对象,然后将他们返回给客户端。
工厂具有下列有点:
松耦合,即对象的创建可以独立于类的实现;客户端无需了解创建对象的类, 但是照样可以使用它来创建对象。它只需要知道需要传递的接口、方法和参数,就能够创建所需类型的对象了。这简化了客户端的实现;
可以轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端 代码。最简单的情况下,客户端只需要传递一个参数就可以了;
工厂还可以重用现有对象。但是,如果客户端直接创建对象的话,总是创建一个新对象。
Factory模式有3种变体
1. 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑
2. 工厂方法模式:允许接口创建对象,但使用那个类来创建对象,则是交由子类决定的。
3. 抽象工厂模式:抽象工厂是一个能创建一系列相关的对象而无需指定/公开某具 体类的接口,该模式能够提供其它工厂的对象,在其内部创建其它对象。
2、简单工厂模式
1、相关概念
-
定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通 常都具有共同的父类。
-
在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂 又被称为静态工厂方法(Static Factory Method).
-
需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实 现过程。
-
例如:我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会 为其提供所需的披萨,如果其所需的我这没有,则是另外的情况,后面会谈。这时 候,我这家披萨店就可以看作工厂(Factory),而生产出来的披萨被称为产品 (Product),披萨的名称被称为参数,工厂可以根据参数的不同返回不同的产品,
这就是简单工厂模式。
2、结构与实现
-
Factory(工厂):核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外 界直接调用,创建所需对象
-
product1/product2(具体产品):简单工厂模式的创建目标,所有被创建的对象都 是某个具体类的实例。它要求实现抽象产品中申明的抽象方法。
-
AbstractProduct(抽象类产品):工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象
3、案例
下面我们使用手机生产来讲解该模式:
IPhone接口:手机标准规范类(AbstractProduct)
/**
* @Author:Shane
* @Date 2020/7/21 21 40
*/
public interface IPhone {
public void make();
}
MiPhone类:制造小米手机(product1)
/**
* @Author:Shane
* @Date 2020/7/21 21 42
*/
public class MiPhone implements IPhone {
public MiPhone(){
this.make();
}
@Override
public void make() {
System.out.println("make xiaomi phone");
}
}
Apple类:制造苹果手机(Product2)
/**
* @Author:Shane
* @Date 2020/7/21 21 46
*/
public class Apple implements IPhone {
public Apple(){
this.make();
}
@Override
public void make() {
System.out.println("make iphone");
}
}
PhoneFactory类:手机代工厂(Factory)
/**
* @Author:Shane
* @Date 2020/7/21 21 48
*/
public class PhoneFactory {
public static IPhone makePhone(String phoneType){
if(phoneType.equals("MiPhone")){
return new MiPhone();
}else if(phoneType.equals("iPhone")){
return new Apple();
}
return null;
}
}
演示:
/**
* @Author:Shane
* @Date 2020/7/21 21 54
*/
public class Test {
public static void main(String[] args) {
IPhone miPhone = PhoneFactory.makePhone("MiPhone");
IPhone iPhone = PhoneFactory.makePhone("iPhone");
}
}
4、简单工厂模式优点
-
工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责
-
客户端无需知道所创建具体产品的类名,只需知道参数即可
-
也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。(这也是我在开始的披萨店里遇到没有的披萨的解决情况)
5、简单工厂模式缺点
-
工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受影响
-
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理 解难度
-
系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成 逻辑过于复杂
-
简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构。
6、适用环境
-
工厂类负责创建对的对象比较少,因为不会造成工厂方法中的业务逻辑过于复杂
-
客户端只知道传入工厂类的参数,对如何创建对象不关心
3、工厂方法模式
和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂,也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产 品,将生产任务交给不同的派生类工厂,这样不用通过指定类型来创建对象了
1、结构与实现
2、案例
接下来继续使用生产手机的例子来讲解该模式。
其中和产品相关的Phone类、MiPhone类和IPhone类的定义不变。
AbstractFactory类:生产不同产品的工厂的抽象类
/**
* @Author:Shane
* @Date 2020/7/21 22 27
*/
public interface AbstractFactory {
public Phone makePhone();
}
XiaoMiFactory类:生产小米手机的工厂(ConcreteFactory1)
/**
* @Author:Shane
* @Date 2020/7/21 22 28
*/
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new MiPhone();
}
}
AppleFactory类:生产苹果手机的工厂(ConcreteFactory2)
/**
* @Author:Shane
* @Date 2020/7/21 22 29
*/
public class AppleFacotry implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
演示:
/**
* @Author:Shane
* @Date 2020/7/21 21 54
*/
public class Test {
public static void main(String[] args) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFacotry();
miFactory.makePhone();
appleFactory.makePhone();
}
}
4、抽象工厂模式
1、定义与特点
前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电 视机、计算机软件学院只培养计算机软件专业的学生等。同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。抽象工厂(AbstractFactory)是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级的产品,而抽象工厂 模式可生产多个等级的产品
使用抽象工厂模式一般需要满足一下条件
-
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品
-
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其它主要优点如下
-
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的 类来进行管理
-
当增加一个新的产品族时不需要修改原代码,满足开闭原则。
2、结构与实现
主要角色如下:
-
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方 法 newProduct(),可以创建多个不同等级的产品。
-
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体 产品的创建。
-
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工 厂模式有多个抽象产品。
-
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来 创建,它 同具体工厂之间是多对一的关系。
抽象产品:冰箱(Fridge)、洗衣机(WashingMachine)
/**
* @Author:Shane
* @Date 2020/7/22 14 43
* 抽象产品:冰箱
*/
public interface Fridge {
public void make();
}
/**
* @Author:Shane
* @Date 2020/7/22 14 51
*/
public interface WashingMachine {
public void make();
}
具体产品:HaierRefrigerator、TCLRefrigerator
/**
* @Author:Shane
* @Date 2020/7/22 14 47
*/
public class HaierRefrigerator implements Fridge {
public HaierRefrigerator(){
this.make();
}
@Override
public void make() {
System.out.println("make a HaierRefrigerator......");
}
}
/**
* @Author:Shane
* @Date 2020/7/22 14 48
*/
public class TCLRefrigerator implements Fridge {
public TCLRefrigerator(){
this.make();
}
@Override
public void make() {
System.out.println("make a TCLRefrigerator......");
}
}
/**
* @Author:Shane
* @Date 2020/7/22 14 52
*/
public class HaierWashingMachine implements WashingMachine {
public HaierWashingMachine(){
this.make();
}
@Override
public void make() {
System.out.println("make a HaierWashingMachine...."=);
}
}
/**
* @Author:Shane
* @Date 2020/7/22 14 53
*/
public class TCLWashingMachine implements WashingMachine {
public TCLWashingMachine(){
this.make();
}
@Override
public void make() {
System.out.println("make a TCLWashingMachine....");
}
}
抽象工厂
/**
* @Author:Shane
* @Date 2020/7/22 14 55
* 抽象工厂
*/
public interface AbstractFactory {
public Fridge newFridge();
public WashingMachine newWashingMachine();
}
具体工厂
/**
* @Author:Shane
* @Date 2020/7/22 14 56
*/
public class HaierFactory implements AbstractFactory {
@Override
public Fridge newFridge() {
return new HaierRefrigerator();
}
@Override
public WashingMachine newWashingMachine() {
return new HaierWashingMachine();
}a
}
/**
* @Author:Shane
* @Date 2020/7/22 15 00
*/
public class TCLFactory implements AbstractFactory {
@Override
public Fridge newFridge() {
return new TCLRefrigerator();
}
@Override
public WashingMachine newWashingMachine() {
return new TCLWashingMachine();
}
}
测试:
/**
* @Author:Shane
* @Date 2020/7/22 15 01
*/
public class Test {
public static void main(String[] args) {
AbstractFactory factory = new HaierFactory();
factory.newFridge();
}
}
2.3 建造者模式
1、建造者模式概念
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
2、案例
比如肯德基麦当劳,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
3、实现
步骤 1 :创建一个表示食物条目和食物包装的接口。
public interface Item {
public String name();
public Packing packing();
public float price();
}
public interface Packing {
public String pack();
}
步骤2:创建实现 Packing 接口的实体类:纸盒和瓶子。
public class Wrapper implements Packing {
@Override
public String pack() {
return "Wrapper";
}
}
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
步骤 3:创建实现 Item 接口的抽象类(汉堡和冷饮),该类提供了默认的功能(纸盒和瓶子)。
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float price();
}
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
}
步骤 4:创建扩展了 Burger 和 ColdDrink 的实体类(素食汉堡和鸡肉汉堡;可口可乐和百事可乐)
public class VegBurger extends Burger {
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "Veg Burger";
}
}
public class ChickenBurger extends Burger {
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "Chicken Burger";
}
}
public class Coke extends ColdDrink {
@Override
public float price() {
return 30.0f;
}
@Override
public String name() {
return "Coke";
}
}
public class Pepsi extends ColdDrink {
@Override
public float price() {
return 35.0f;
}
@Override
public String name() {
return "Pepsi";
}
}
步骤 5:创建一个 Meal 类,带有上面定义的 Item 对象,进行套餐条目展示和金额。
import java.util.ArrayList;
import java.util.List;
public class Meal {
private List<Item> items = new ArrayList<Item>();
public void addItem(Item item){
items.add(item);
}
public float getCost(){
float cost = 0.0f;
for (Item item : items) {
cost += item.price();
}
return cost;
}
public void showItems(){
for (Item item : items) {
System.out.print("Item : "+item.name());
System.out.print(", Packing : "+item.packing().pack());
System.out.println(", Price : "+item.price());
}
}
}
步骤 6:创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象,创建不同的套餐(Meal)
public class MealBuilder {
public Meal prepareVegMeal (){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
public Meal prepareNonVegMeal (){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
public Meal prepareAllMeal(){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
测试:
public class BuilderPatternDemo {
public static void main(String[] args) {
//建造者
MealBuilder mealBuilder = new MealBuilder();
//建造素食套餐
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("Veg Meal");
vegMeal.showItems();
System.out.println("Total Cost: " +vegMeal.getCost());
//建造非素食套餐
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
System.out.println("nNon-Veg Meal");
nonVegMeal.showItems();
System.out.println("Total Cost: " +nonVegMeal.getCost());
//建造全家桶套餐
Meal allMeal = mealBuilder.prepareAllMeal();
System.out.println("nAll Meal");
allMeal.showItems();
System.out.println("Total Cost: " +allMeal.getCost());
}
最后
以上就是伶俐山水为你收集整理的设计模式-创建型的全部内容,希望文章能够帮你解决设计模式-创建型所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复