关注的问题:创建对象,将对象的创建和使用分离,这样的目的可以降低代码的耦合度的问题
单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式
单例模式(Singleton Pattern)
定义:
单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个访问它的全局访问点
单例类图:
单例中包含的角色:
单例类:包含一个实例且能自行创建这个实例类
单例模式特点:
1.单例类只有一个实例对象
2.单例对象必须由单例类自行创建
3.单例类对外提供一个可访问该单例的全局访问点
代码演示
单例的实现有8种
饿汉式(静态常量)
饿汉式(静态代码块)
懒汉式
双重检查
静态内部类
枚举
饿汉式(静态常量)
/**
* 饿汉式(静态变量)
* 线程安全的,类加载到内存中后,有JVM保存线程安全
* 简单,推荐实现
*/
public class SingLeton1 {
//构造函数私有化,防止new对象
private SingLeton1(){}
//本类内部创建对象,静态变量
private final static SingLeton1 singLeton1=new SingLeton1();
//提供一个公有的静态方法,返回实例对象,提供给外部使用
public static SingLeton1 getInstance(){
return singLeton1;
}
}
优缺点:
优点:写法简洁,在类加载时已经完成类的实例化,保证线程安全
缺点:在类加载时完成了实例化(没有实现懒加载),如果没有使用这个实例,会造成内存的浪费
饿汉式(静态代码块)
/**
* 饿汉式(静态代码块)
*/
public class SingLeton2 {
//构造函数私有化
private SingLeton2(){}
//类内部创建对象实例
private static SingLeton2 singLeton2;
//在静态代码块中,创建单例对象
static{
singLeton2=new SingLeton2();
}
//提供公有的访问点
public static SingLeton2 getInstance(){
return singLeton2;
}
}
优缺点同上
懒汉式(线程不安全)
/**
* 懒汉式,线程不安全
*/
public class SingLeton3 {
private static SingLeton3 singLeton3;
private SingLeton3(){}
//提供一个公有的访问,当调用时,才创建实例,即懒汉式
public static SingLeton3 getInstance(){
if(singLeton3==null){
singLeton3=new SingLeton3();
}
return singLeton3;
}
}
优点:实现了懒加载的效果,但是只能在单线程使用
缺点:在多线程下,存在线程不安全
懒汉式(线程安全,同步方法)
public class SingLeton4 {
private static SingLeton4 singLeton4;
private SingLeton4(){}
public static synchronized SingLeton4 getInstance(){
if(singLeton4==null){
singLeton4=new SingLeton4();
}
return singLeton4;
}
}
线程安全的关键点:synchronized
优点:通过线程安全的关键字,解决线程安全问题
缺点:效率低下,每个线程在获取实例的时候,执行getInstance都需要进行同步,方法的同步效率太低
懒汉式(线程安全,同步代码块)
public class SingLeton5 {
private static SingLeton5 singLeton5;
private SingLeton5(){}
public static SingLeton5 getInstance(){
if(singLeton5==null){
synchronized (SingLeton5.class){
singLeton5=new SingLeton5();
}
}
return singLeton5;
}
}
对加锁做了一定优化,总体优缺点和上面一致
双重检查
/**
* 双重检验
*/
public class SingLeton6 {
private static SingLeton6 singLeton6;
private SingLeton6(){}
public static synchronized SingLeton6 getInstance(){
if(singLeton6==null){
synchronized (SingLeton6.class){
if(singLeton6==null){
singLeton6=new SingLeton6();
}
}
}
return singLeton6;
}
}
优缺点:
双重检查进行了两次的if (single == null)检查,保证线程安全性
线程安全,延时加载,效率高,推荐使用
静态内部类
/**
* 静态内部类
* 静态内部属性,在类加载时进行初始化,保证线程安全
* 采用静态内部类实现延迟加载
*/
public class SingLeton7 {
private SingLeton7(){}
//提供一个静态内部类,该类中有一个静态属性
private static class SingleHolder{
private static SingLeton7 singLeton7=new SingLeton7();
}
//提供一个公有的静态访问
public static SingLeton7 getInstance() {
return SingleHolder.singLeton7;
}
}
优缺点:
线程安全:本质是通过类加载来保证线程安全
实现懒加载:只有在实际使用的时候,才会触发类的初始化,也是懒加载的一种形式
效率高:没有使用锁机制
推荐使用
枚举方式
/**
* 枚举方式实现单例
*/
public enum SingLeton8 {
INSTANCE;//属性
//提供一个静态的公有的访问
public static SingLeton8 getInstance() {
return INSTANCE;
}
}
推荐使用
单例再探究
优点:
单例模式保证在内存中只存在一个实例,减少了内存的开销
避免了对资源的多种占用
单例设置全局访问点,可以优化和共享资源的访问
缺点:
单例模式一般没有接口,扩展困难,如果要进行扩展,就需要修改代码,违背了开闭原则
适用场景:
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC
某些对象实例占用资源比较多,或者实例耗时较长且经常使用的对象,采用单例形式
Java应用
1、spring中的单例
在spring中,Bean可以被定义两种模式:Singleton(单例)和Prototype(多例),Spring中模式采用的单例
工厂方法模式(Factory Method)
按照业务场景划分:工厂模式有三种不同的实现:分别是简单工厂、工厂方法模式和抽象工厂模式
简单工厂模式
简单工厂介绍
简单工厂有一个具体的工厂类,可以生产不同的产品,属于创建型设计模式
注意:简单工厂模式不属于23种设计模式之列
简单工厂类图UML
简单工厂角色说明:
简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑,工厂类的创建产品类的方法可以被外界直接访问,创建所需的产品对象
抽象产品(Product):是简单工厂创建所有对象的父类,负责描述所有实例公有的公共接口
具体产品(ConcreteProduct):是简单工厂创建的具体对象
代码实现:
简单工厂实现:
/**
* 抽象产品,提供所有具体产品的共有的方法
*/
public interface Product {
public void show();
}
/**
* 具体产品1
*/
public class ConcreteProduct1 implements Product {
@Override
public void show() {
System.out.println("具体产品1展示...");
}
}
/**
* 具体产品2
*/
public class ConcreteProduct2 implements Product {
@Override
public void show() {
System.out.println("具体产品2展示。。。");
}
}
/**
* 简单工厂
*/
public class SimpleFactory {
//提供一个方法,基于不同的参数获取不同的实例对象
public static Product getConcreteProduct(int productType) {
switch (productType) {
case Const.Product1:
return new ConcreteProduct1();
case Const.Product2:
return new ConcreteProduct2();
}
return null;
}
final class Const {
static final int Product1 = 1;//表示是ConcreteProduct1产品
static final int Product2 = 2;//表示是ConcreteProduct2产品
}
调用方式:
//获取产品1
Product product = SimpleFactory.getConcreteProduct(Const.Product1);
product.show();
//获取产品2
Product product1 = SimpleFactory.getConcreteProduct(Const.Product2);
product1.show();
使用步骤:
1.创建抽象产品类和定义具体产品的公共接口
2.创建具体产品类,定义生产的具体产品
3.创建工厂类,通过静态方法根据传入参数的不同从而创建不同的具体产品的实例
4.外界通过调用工厂类的静态方法,传入不同的参数创建不同的产品实例
优缺点:
优点:
1、代码逻辑比较简单,工厂类包含必要的创建对象的逻辑,可以决定创建具体的产品
2、调用方无需知道所创建具体产品的类名,只需要知道参数即可
缺点:
简单工厂违背开闭原则,工厂类负责产品的创建职责过重,一旦新增产品需要对工厂类内部逻辑新增判断
系统扩展困难,产品过多会造成逻辑过于复杂
使用场景:
对于产品种类比较少的情况,可以考虑使用简单工厂,调用方只需要传入工厂类的参数,不需要关注如何创建的逻辑
工厂方法模式
工厂方法模式介绍
定义:工厂方法模式是对简单工厂模式的进一步的抽象化,优点是可以使系统不修改原来代码的情况下引进新的产品,即满足开闭原则
定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到子类中
工厂方法模式类图UML:
工厂方法模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,调用方通过它访问具体工厂的工厂方法来创建产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,由具体工厂来创建产品,具体工厂和具体产品一一对应
代码实现:
/**
* 抽象产品:提供具体产品的共有方法
*/
public interface Product {
public void show();
}
public class ConcreteProduct1 implements Product {
@Override
public void show() {
System.out.println("具体产品1展示。。。");
}
}
public class ConcreateProduct2 implements Product {
@Override
public void show() {
System.out.println("具体产品2展示...");
}
}
/**
* 抽象工厂
*/
public interface AbstractFactory {
public Product createProduct();
}
/**
* 具体工厂1,产生具体产品1
*/
public class ConcreteFactory1 implements AbstractFactory {
@Override
public Product createProduct() {
ConcreteProduct1 concreteProduct1 = new ConcreteProduct1();
System.out.println("具体工厂1创建具体产品1...");
return concreteProduct1;
}
/**
* 具体工厂2,产生具体产品2
*/
public class ConcreteFactory2 implements AbstractFactory {
@Override
public Product createProduct() {
System.out.println("具体工厂2产生具体产品2...");
return new ConcreateProduct2();
}
}
调用:
//获取具体产品1
Product product = new ConcreteFactory1().createProduct();
product.show();
//获取具体产品2
Product product1 = new ConcreteFactory2().createProduct();
product1.show();
使用步骤:
1、创建抽象产品类,定义产品的公共方法
2、创建具体产品类(实现抽象产品接口),定义生成的具体产品
3、创建抽象工厂类,定义具体工厂的公共接口
4、创建具体工厂类,定义创建对应具体产品实例的方法
5、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
工厂模式再探究:
解决问题:
解决了简单工厂类新增产品需要修改工厂类的方法逻辑问题,即满足开闭原则。
将具体的产品创建给到工厂类的子类(具体工厂),此时工厂类不在负责所有产品的创建,而是给出具体工厂必须实现的接口,这样工厂方法在添加新的产品的时候就不需要修改工厂类的逻辑而是添加了新的工厂子类,符合开闭原则
优点:
灵活性强,对于新产品的创建,只需要多写一个对应的工厂类
用户只需要知道工厂的名称就可以获得所要的产品,不需要知道产品具体创建过程
缺点:
类的个数容易过多,增加了复杂度
每一个工厂只能生产一种产品,次弊端可以使用抽象工厂模式解决
应用场景:
客户只关注创建产品的工厂名,不需要知道具体的产品名称
抽象工厂模式(Abstract Factory)
定义:
提供一个创建一系列相关或者互相依赖对象的接口,无需指定他们的具体的类,具体的工厂负责实现具体的产品的实例
使一个工厂可以生产多个产品
抽象工厂类图UML
抽象工厂模式的主要角色:
抽象工厂(AbstractFactory):提供了创建产品的接口,包含了多个创建产品的接口方法,可以创建多个不同等级的产品
具体工厂(ConcreteFactory):实现了抽象工厂定义的方法,完成具体产品的创建
抽象产品(Product):定义了产品的规范,描述产品的主要特征和性能
具体产品(ConcreteProduct):实现了抽象产品的定义的方法,有具体工厂来创建产品,具体工厂和具体产品一对多关系
代码实现:
/**
* 抽象工厂:提供具体工厂的所有方法
*/
public interface AbstractFactory {
public ConcreteProduct1 createProduct1();
public ConcreteProduct2 createProduct2();
}
/**
* 具体工厂1
*/
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ConcreteProduct1 createProduct1() {
System.out.println("具体工厂1生产具体产品1");
ConcreteProduct1 product11 = new ConcreteProduct1();
return product11;
}
@Override
public ConcreteProduct2 createProduct2() {
System.out.println("具体工厂1生产具体产品2");
ConcreteProduct2 product12 = new ConcreteProduct2();
return product12;
}
}
/**
* 具体工厂2
*/
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ConcreteProduct1 createProduct1() {
System.out.println("具体工厂2生产具体产品1");
ConcreteProduct1 product21 = new ConcreteProduct1();
return product21;
}
@Override
public ConcreteProduct2 createProduct2() {
System.out.println("具体工厂2生产具体产品2");
ConcreteProduct2 product22 = new ConcreteProduct2();
return product22;
}
}
/**
* 抽象产品:提供具体产品的所有方法
*/
public interface product {
public void show();
}
/**
* 具体产品1
*/
public class ConcreteProduct1 implements product {
@Override
public void show() {
System.out.println("具体产品1展示");
}
}
/**
* 具体产品2
*/
public class ConcreteProduct2 implements product {
@Override
public void show() {
System.out.println("具体产品2展示");
}
}
调用:
public static void main(String[] args) {
//从具体工厂1获取具体产品1
ConcreteProduct1 product1 = new ConcreteFactory1().createProduct1();
product1.show();
//从具体工厂1获取具体产品2
ConcreteProduct2 product2 = new ConcreteFactory1().createProduct2();
product2.show();
//从具体工厂2获取具体产品1
ConcreteProduct1 product3 = new ConcreteFactory2().createProduct1();
product3.show();
//从具体工厂2获取具体产品2
ConcreteProduct2 product4 = new ConcreteFactory2().createProduct2();
product4.show();
}
使用步骤:
1、创建抽象产品类,定义产品的公共方法
2、创建具体产品类(实现抽象产品接口),定义生成的具体产品
3、创建抽象工厂类,定义具体工厂的公共接口,一个工厂类可以创建不同等级的产品
4、创建具体工厂类,定义创建对应具体产品实例的方法
5、调用方调用具体的工厂类的方法,从而创建不同具体产品的实例
解决问题:
一个工厂可以创建多个产品,解决了一个工厂只能创建一个产品的缺点
优点:
可以在工厂类内部对产品族中相关联的多级产品共同管理,而不必专门引入新的类来管理
抽象工厂增加了程序的可扩展性,当新增一个新的产品族时,不需要修改源代码,满足开闭原则
缺点:
当产品族新增一个产品类型时,所有的工厂类都需要进行修改,增加了系统的抽象性和理解难度
JAVA应用
在Spring框架中对简单工厂的使用
SPring中所有对象通过IOC容器进行管理,获取对象通过getBean
使用示例如下:
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext(path);
//在IOC容器获取需要的对象实例
Student student = (Student) applicationContext.getBean("student");
当前的ClassPathXmlApplicationContext是ApplicationContext容器的实现,而ApplicationContext是BeanFactory接口的子接口,BeanFactory接口是简单工厂模式的使用,通过其提供的BeanFactory方法可以获取更多的对象