3.16 常用设计模式(单例,工厂)与Spring001

一、设计模式不是Java独有的。它是一套代码设计经验的总结,项目中合理的运用设计模式可以巧妙的解决很多问题。但要对设计模式的使用场景有一定的认识后才使用,不要滥用。

总体来说设计模式可以分为三大类:创建型模式(工厂方法模式、抽象工厂模式、单例模式在其中),结构型模式(代理模式在其中),行为型模式(观察者模式在其中)。今天学的设计模式包括单例设计模式工厂模式

1.单例模式指的是一个类只能有一个实例,这样的类被称为单例类,或者单态类,即Singleton Class。

单例类的特点

  • 单例类只可有一个实例
  • 它必须自己创立这唯一的一个实例
  • 它必须给所有其它的类提供自己这一实例

应用场景:

需要频繁的进行创建和销毁的对象;---数据库连接类

创建对象时耗时过多或耗费资源过多,但又经常用到的对象;

工具类对象;

频繁访问数据库或文件的对象。

 

Java中实现单例模式可以通过两种形式实现:

懒汉模式类加载时不初始化,等到需要时再进行初始化)

饿汉模式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)

单例类的实现步骤:

1、构造方法是private权限,保证其他类无法创建该类实例,只能该类自身创建

2、声明一个static修饰的自身实例,保证该实例永远只是一个

3、提供一个public方法,返回定义的static自身实例

相关代码:

package designpatterndemo;
/** 
 * @author hyy
 * @ClassName:Singleton
 * @Description: 单例类的特点 单例类只可有一个实例 
 * 它必须自己创立这唯一的一个实例 它必须给所有其它的类提供自己这一实例  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public class Singleton {
	//2 当前类内部实例化对象(JVM加载这个类,就实例化,立即加载) 饿汉
	private static Singleton single=new Singleton();
	
	/**
	 * 1 构造方法私有化(外部不能直接使用new创建对象)
	 */
	private Singleton() {
		System.out.println("私有的构造方法");
	}
	
	/**
	 * 3 创建一个静态的方法,返回值为第二步创建的那个对象
	 * @return
	 */
	public static Singleton getInstance() {
		return single;
	}
}

JVM进行类加载,不会出现线程不安全的问题。

public class Singleton2 {
	//2 当前类内部实例化对象
	private static Singleton2 single=null;
	
	/**
	 * 1 构造方法私有化(外部不能直接使用new创建对象)
	 */
	private Singleton2() {
		System.out.println("私有的构造方法");
	}
	
	/**
	 * 3 创建一个静态的方法,返回值为第二步创建的那个对象
	 * 当用户第一次获取对象,才实例化.(用到了才实例,懒加载)
	 * 懒汉
	 * @return
	 */
	public synchronized static Singleton2 getInstance() {
		//加synchronized关键字保证线程同步
		if(single==null) {
			// 第一次访问
			single=new Singleton2();
		}
		return single;
	}
}

而懒汉模式下有线程不安全的可能,因此在静态方法名前加入synchronized关键字以保证线程安全。不过,整个方法被同步,效率相对较低,还有优化空间。

package designpatterndemo;
/** 
 * @author hyy
 * @ClassName:类名
 * @Description:单例类的特点 单例类只可有一个实例 
 * 它必须自己创立这唯一的一个实例 它必须给所有其它的类提供自己这一实例
 * 改造:效率高,线程同步.
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public class Singleton3 {
	//私有静态内部类
	//由JVM装载类的时候进行,保证了线程的安全性
	private static class SingletonHolder{
		//静态初始化将在实例被任何线程访问到之前对其进行初始化
		//2 当前类内部实例化对象
		public static Singleton3 single=new Singleton3();
	} 
	
	/**
	 * 1 构造方法私有化(外部不能直接使用new创建对象)
	 */
	private Singleton3() {
		System.out.println("私有的构造方法");
	}
	
	/**
	 * 3 创建一个静态的方法,返回值为第二步创建的那个对象 
	 * 当用户第一次获取对象,才实例化.(用到了才实例,懒加载) 懒汉
	 * 
	 * @return
	 */
	//对外提供公有方法,返回对象实例
	//(只有在调用该公有方法getInstance()的时候才会去装载静态内部类,
	//实例化外部类对象,实现了懒加载,
	//而且外部类对象的实例化是基于JVM对内部类的装载机制,
	//类装载只装载一次,保证单例,同时类装载是线程同步的所以是线程安全的)
	//final是为了不被重写,static是为了对象直接调用
	public final static Singleton3 getInstance() {
		//第一次访问时加载SingletonHolder类并初始化实例
		return SingletonHolder.single;
	}
}

 

静态初始化将在实例被任何线程访问到之前对其进行初始化。JVM进行类加载时,私有静态内部类Singleton也会被加载,从而确保了线程的安全。public方法可以直接通过类名.对象名获取实例。

 

相关的单例设计模式的类:Runtime类,Toolkit类等等。特点:

  • 没有公开的构造方法
  • 提供静态方法返回该类实例
  • 使用类Testxxx.java测试,获得多个实例,输出后可见虚地址相同,说明是同一个实例

 

2.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

工厂模式一般分为三类

简单工厂模式(Simple Factory)

工厂方法模式(Factory Method)

抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。

简单工厂模式又称静态工厂方法模式。它存在的目的很简单:定义一个用于创建对象的接口。

工厂类角色 这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现

抽象产品角色 它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。

具体产品角色 工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

以汉堡为例,假设一家店卖不同餐厅的汉堡(金拱门,肯德基,华莱士等等)

相关代码:

package factorypattern;
/** 
 * @author hyy
 * @ClassName: Hamburger
 * @Description:抽象商品  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public abstract class Hamburger {
	//考虑汉堡有多种品牌,不可能一个个创建对象,
	//工厂类角色用具体类实现,
	//抽象产品角色父类汉堡由接口或者抽象类来实现,
	//具体产品角色子类(店的汉堡)在java中由一个具体类实现
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Hamburger [name=" + name + "]";
	}
}

/**
 * 
 * @ClassName: MDLHam
 * @Description:具体商品
 * @author knowno
 * @date 2022年3月16日 上午11:22:34
 *
 */
class MDLHam extends Hamburger {
	public MDLHam() {
		super.setName("麦当劳汉堡");
	}
}

/**
 * 
 * @ClassName: KFCHam
 * @Description: 具体商品
 * @author knowno
 * @date 2022年3月16日 上午11:22:45
 *
 */
class KFCHam extends Hamburger {
	public KFCHam() {
		super.setName("肯德基汉堡");
	}
}

/**
 * 
 * @ClassName: HLSHam
 * @Description: 具体商品
 * @author knowno
 * @date 2022年3月16日 上午11:22:49
 *
 */
class HLSHam extends Hamburger {
	public HLSHam() {
		super.setName("华莱士汉堡");
	}
}

以上是抽象产品(汉堡)和具体产品(不同餐厅的汉堡),下面是工厂类角色(用于生产产品)

package factorypattern;
/** 
 * @author hyy
 * @ClassName:SimpleFactory
 * @Description:生产产品   工厂类角色
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public class SimpleFactory {
	private SimpleFactory() {
		// TODO Auto-generated constructor stub
	}
	
	/**
	 * 
	 * @param param
	 * @return
	 */
	public static Hamburger createObject(String param) {
		if("mdl".equals(param)) {
			return new MDLHam();
		}else if("kfc".equals(param)) {
			return new KFCHam();
		}else if("hls".equals(param)) {
			return new HLSHam();
		}else {
			return null;
		}
	}
}

可以从开闭原则上来分析简单工厂模式。当餐厅增加了一个新餐厅的汉堡的时候,只要符合抽象产品制定的合同,那么只要通知工厂类知道就可以被餐厅使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种汉堡,都要在工厂类中增加相应的业务逻辑或者判断逻辑,这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。由此,简单工厂模式进一步抽象化和推广为工厂方法模式,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。

抽象工厂角色:

工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

具体工厂角色:

含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

抽象产品角色:

它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

具体产品角色:

具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

相关代码:

package factorypattern;
/** 
 * @author hyy
 * @ClassName:类名
 * @Description:描述  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */

/**
 * 
 * @ClassName: AbstractFactory2
 * @Description: 抽象工厂
 * @author knowno
 * @date 2022年3月16日 下午2:23:32
 *
 */
abstract class AbstractFactory0{
	abstract Hamburger createHam();
}

/**
 * 
 * @ClassName: KFCFactory
 * @Description: KFCFactory(具体工厂)
 * @author knowno
 * @date 2022年3月16日 下午2:24:51
 *
 */
class KFCFactory extends AbstractFactory0{

	@Override
	Hamburger createHam() {
		// TODO Auto-generated method stub
		return new KFCHam();
	}
}
/**
 * 
 * @ClassName: MDLFactory 
 * @Description: (具体工厂)
 * @author knowno  
 * @date 2022年3月16日 下午2:25:46 
 *
 */
class MDLFactory extends AbstractFactory0{

	@Override
	Hamburger createHam() {
		// TODO Auto-generated method stub
		return new MDLHam();
	}
}
/**
 * 
 * @ClassName: HLSFactory 
 * @Description: (具体工厂)
 * @author knowno  
 * @date 2022年3月16日 下午2:25:46 
 *
 */
class HLSFactory extends AbstractFactory0{

	@Override
	Hamburger createHam() {
		// TODO Auto-generated method stub
		return new HLSHam();
	}
}

public class FactoryMethod{
	public static void main(String[] args) {
		AbstractFactory0 af=new MDLFactory();
		System.out.println(af.createHam());
	}
}

再拿车来举例,一个土豪有很多车,因为钱up,他买了几辆豪车-兰博基尼、保时捷、悍马…,由于车辆增加了,所有的汽车都要由司机一个人管理,年检、保险、罚单、洗车,司机一个人实在吃不消,而且老板会吩咐司机从周一到周六要开不同款式的车外出,这个时候司机就不得不记住老板哪天需要开哪辆车外出,他觉得太困难了,于是乎给老板提议:给每台车都聘请一个司机,每辆汽车都有一个专门的人员负责,需要外出的时候招呼我一声,我就会派相应的人员过来接老板,老板毫不犹豫的说:就这么办.......

代码:

package factorypattern;
/** 
 * @author hyy
 * @ClassName:类名
 * @Description:描述  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */

/**
 * 
 * @ClassName: MyCar 
 * @Description: 抽象产品与具体产品
 * @author knowno  
 * @date 2022年3月16日 下午2:19:38 
 *
 */
interface MyCar{
	public void drive();
}

class Benz implements MyCar{
	@Override
	public void drive() {
		System.out.println("Driving Benz");	
	}
}

class Bmw implements MyCar{
	@Override
	public void drive() {
		System.out.println("Driving Bmw");	
	}
}

class Audi implements MyCar{
	@Override
	public void drive() {
		System.out.println("Driving Audi");	
	}
}

/**
 * 
 * @ClassName: Driver 
 * @Description: 抽象工厂
 * @author knowno  
 * @date 2022年3月16日 下午2:20:09 
 *
 */
interface Driver{
	public MyCar driverMyCar();
}

class BenzDriver implements Driver{
	@Override
	public MyCar driverMyCar() {
		// TODO Auto-generated method stub
		return new Benz();
	}
}

class BmwDriver implements Driver{
	@Override
	public MyCar driverMyCar() {
		// TODO Auto-generated method stub
		return new Bmw();
	}
}

class AudiDriver implements Driver{
	@Override
	public MyCar driverMyCar() {
		// TODO Auto-generated method stub
		return new Audi();
	}
}

public class FactoryMethod2 {
	public static void main(String[] args) {
		try {
			Driver driver=new BmwDriver();
			MyCar MyCar=driver.driverMyCar();
			MyCar.drive();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

PS:使用工厂方法模式足以应付我们可能遇到的大部分业务需求。但是当产品种类非常多时,就会出现大量的与之对应的工厂类,这不应该是我们所希望的。所以建议在这种情况下使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。

 

事实上很多时候工厂所产生的产品会划分为很多层次。

产品族

位于不同产品等级结构中,功能相关联的产品组成的家族。

如果项目中涉及到多个产品族,则可以使用抽象工厂模式,抽象工厂模式的各个角色和工厂方法的如出一辙:

抽象工厂角色

这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

具体工厂角色

它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。

抽象产品角色

它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

具体产品角色

具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

相关代码:

package factorypattern;

//车有很多种,也分品牌,构成产品族,车为接口,跑车和商务车为车的子接口,
//不同品牌的跑车和商务车为车两个子接口的实现类
//抽象工厂作为接口提供跑车和商务车两种选择,不同品牌的具体工厂实现工厂接口的方法

//使用抽象工厂模式还要满足一下条件:
//系统中有多个产品族,而系统一次只可能消费其中一族产品 
//同属于同一个产品族的产品一起使用

/**
 * 
 * @ClassName: Cars 
 * @Description: 抽象产品
 * @author hyy
 * @date 2022年3月16日 下午2:32:00 
 *
 */
//有相同的Car.class文件,所以接口命名改为Cars
interface Cars{
	public void drive();
}


/**
 * 
 * @ClassName: SportsCar 
 * @Description: 抽象产品(跑车)
 * @author hyy
 * @date 2022年3月16日 下午2:32:12 
 *
 */
interface SportsCar extends Cars{
	//是否敞篷车
	public boolean isConvertible();
}

/**
 * 
 * @ClassName: BussinessCar 
 * @Description: 抽象产品(商务车)
 * @author hyy
 * @date 2022年3月16日 下午2:32:47 
 *
 */
interface BusinessCar extends Cars{
	//是否自动门
	public boolean isAutoDoor();
}

/**
 * 
 * @ClassName: BenzSprotsCar 
 * @Description: 奔驰跑车系列(具体商品)
 * @author hyy
 * @date 2022年3月16日 下午2:33:32 
 *
 */
class BenzSprotsCar implements SportsCar {
	public void drive() {
		System.out.println("Driving Benz S");
	}

	@Override
	public boolean isConvertible() {
		return true;
	}
}

/**
 * 
 * @ClassName: BenzBussinessCar 
 * @Description: 奔驰商务车系列(具体商品)
 * @author hyy  
 * @date 2022年3月16日 下午2:34:14 
 *
 */
class BenzBusinessCar implements BusinessCar{

	@Override
	public void drive() {
		System.out.println("Driving Benz BusinessCar");
	}

	@Override
	public boolean isAutoDoor() {
		// TODO Auto-generated method stub
		return false;
	}
}

/**
 * 
 * @ClassName: BmwSprotsCar 
 * @Description: 宝马跑车系列(具体商品)
 * @author hyy
 * @date 2022年3月16日 下午2:34:59 
 *
 */
class BmwSportsCar implements SportsCar {

	@Override
	public void drive() {
		System.out.println("Driving Bmw SportsCar");		
	}

	@Override
	public boolean isConvertible() {
		// TODO Auto-generated method stub
		return false;
	}
}

/**
 * 
 * @ClassName: BmwBussinessCar 
 * @Description: 宝马商务车系列(具体商品)
 * @author hyy
 * @date 2022年3月16日 下午2:35:10 
 *
 */
class BmwBusinessCar implements BusinessCar {

	@Override
	public void drive() {
		System.out.println("Driving Bmw BusinessCar");	
	}

	@Override
	public boolean isAutoDoor() {
		// TODO Auto-generated method stub
		return false;
	}	
}

/**
 * 
 * @ClassName: CarFactory 
 * @Description: 抽象工厂
 * @author hyy
 * @date 2022年3月16日 下午2:35:23 
 *
 */
interface CarFactory{
	public SportsCar getSportsCar();
	public BusinessCar getBusinessCar();
}

/**
 * 
 * @ClassName: BenzCarFactory 
 * @Description: 奔驰工厂(具体工厂)
 * @author hyy
 * @date 2022年3月16日 下午2:35:35 
 *
 */
class BenzCarFactory implements CarFactory{

	@Override
	public SportsCar getSportsCar() {
		// TODO Auto-generated method stub
		return new BenzSprotsCar();
	}

	@Override
	public BusinessCar getBusinessCar() {
		// TODO Auto-generated method stub
		return new BenzBusinessCar();
	}
} 

/**
 * 
 * @ClassName: BmwCarFactroy 
 * @Description: 宝马工厂(具体工厂) 
 * @author hyy  
 * @date 2022年3月16日 下午2:35:51 
 *
 */
class BmwCarFactroy implements CarFactory{

	@Override
	public SportsCar getSportsCar() {
		// TODO Auto-generated method stub
		return new BmwSportsCar();
	}

	@Override
	public BusinessCar getBusinessCar() {
		// TODO Auto-generated method stub
		return new BmwBusinessCar();
	}
}

class AbstractCarFactory{
	public static CarFactory getCarFactory(String name){
		if("Benz".equals(name)) {
			return new BenzCarFactory();
		}else if("Bmw".equals(name)){
			return new BmwCarFactroy();
		}else{
			throw new RuntimeException();
		}
	}
}


public class AbstractFactory {

	public static void main(String[] args) {
		try {
			CarFactory factory=AbstractCarFactory.getCarFactory("Bmw");
			BusinessCar car = factory.getBusinessCar();
			car.drive();
			System.out.println(car.isAutoDoor());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

说实话,抽象工厂远比单例设计模式和简单工厂模式要难得多。

抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。而且使用抽象工厂模式还要满足以下条件

系统中有多个产品族,而系统一次只可能消费其中一族产品

同属于同一个产品族的产品一起使用

在抽象工厂模式中,抽象产品可能是一个或多个,从而构成一个或多个产品族。而在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。

 

Spring的基本结构

Spring的优点:非常轻量级的容器,低侵入,代码污染低。独立于各种应用服务器.DI(IOC)降低了业务对象替换复杂性;spring调度。

没学Spring之前,我们做web项目是通过ajax和后端控制层的servlet交互,servlet调用service方法,service调用dao方法,dao通过JDBC连接到Mysql数据库。学了Spring后,servlet的代码被Spring封装,成为SpringMVC模块;service事务管理被Spring事务管理取代;dao层写的sql代码被Mybatis框架所代替。

初次接触Spring

创建一个Java Project,导入下图中的jar包(build path)。

创建一个实体类Blog

package com.etc.entity;

import java.io.Serializable;

/** 
 * @author hyy
 * @ClassName:类名
 * @Description:描述  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public class Blog implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 3624211812642008714L;
	private int id;
	private String title;
	private String author;
	
	public Blog() {
		// TODO Auto-generated constructor stub
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}
}

配置Spring文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- java对象的管理开始 -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- bean创建 上面的代码是安装Spring插件后可以自动生成的-->
	<bean id="blog" class="com.etc.entity.Blog"></bean>
</beans>

测试配置后能否取到blog对象

package com.etc.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.etc.entity.Blog;

/** 
 * @author hyy
 * @ClassName:类名
 * @Description:描述  
 * @date 2022年3月15日 上午11:15:56 
 * @parameter 参数及其意义  
 * @return 返回值  
 */
public class TestBlog {

	public static void main(String[] args) {
		//创建ClassPathXmlApplicationContext
		ApplicationContext context=new ClassPathXmlApplicationContext("ApplicationContext.xml");;
		//找spring容器拿 xml里bean配置的id
		//Blog blog = (Blog) context.getBean("blog");
		
		for (int i = 1; i <= 100; i++) {
			System.out.println(context.getBean("blog"));
		}
		//运行发现输出结果都是 com.etc.entity.Blog@1bce4f0a
		//而构造只执行一次;而且默认情况下,构造是在加载配置文件就执行的,
		//所以,我们说,这是单例模式的一种应用,饿汉,立即加载[加载配置文件就构造].
	}
}

 

今日总结:学的大都能理解并敲出来,只是抽象工厂模式有点绕,需要再练练。

posted on 2022-03-16 19:52  heyiyang1312  阅读(14)  评论(0)    收藏  举报

导航