单例设计模式
- 指一个类只有一个实例,且该类能自行创建这个实例的一种模式
- Java中Runtime的源码就是使用单例模式实现的,初次之外还有Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
- 特点:
- 单例类只有一个实例对象
- 该单例对象必须由单例类自行创建
- 单例类对外提供一个访问该单例的全局访问点
- 注意事项和细节说明:
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
- 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂 等)
1、饿汉式
- 步骤:
- 构造器私有化(防止 new)
- 类的内部创建对象
- 向外暴露一个静态的公共方法
- 优缺点说明:
- 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
- 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
- 这种方式基于classloder(类装载)机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但 是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading(懒加载)的效果
- 结论:这种单例模式可用,可能造成内存浪费
//饿汉式(使用静态变量实现)
class Singleton {
//1.构造器私有化,外部能new
private Singleton() {}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance ;
}
}
//饿汉式(使用静态代码块实现)
class Singleton {
//1.构造器私有化,外部能new
private Singleton() {}
//2.本类内部创建对象实例
private static Singleton instance;
//在景泰代码块中创建单例对象
static{
instance = new Singleton();
}
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance ;
}
}
2、懒汉式
第一种写法(线程不安全)
- 优缺点说明:
- 起到了Lazy Loading(懒加载)的效果,但是只能在单线程下使用。
- 如果在多线程下,一个线程进入了if (singleton = null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以.在多线程环境下不可使用这种方式
- 结论:在实际开发中,不要使用这种方式。
//懒汉式(线程不安全)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
public static Singleton getInstance() {
if(instance == nu11) {
instance = new Singleton();
}
return instance;
}
}
第二种写法(线程安全,同步方法)
- 优缺点说明:
- 解决了线程不安全问题
- 效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
- 结论:在实际开发中,不推荐使用这种方式
//懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入同步处理方法,解决线程安全问题
public static synchronized Singleton getInstance() {
if(instance == nu11) {
instance = new Singleton();
}
return instance;
}
}
第三种写法(双重检查)
- 优缺点说明:
- Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == nul)检查,这样就可以保证线程安全了。
- 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null), 直接return实例化对象,也避免的反复进行方法同步.
- 线程安全;延迟加载;效率较高
- 结论:在实际开发中,推荐使用这种单例设计模式
//懒汉式(双重检查)
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题,同时保证了效率
public static synchronized Singleton getInstance() {
if(instance == null) {
synchronized(Singletion.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
}
3、应用场景
- 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
- 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
4、扩展
- 单例模式可扩展为有限的多例(Multitcm)模式,这种模式可生成有限个实例并保存在 ArmyList 中,客户需要时可随机获取
原型模式
在有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效
1、定义与特点
- 定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节
2、结构与实现
- 模式的结构:
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
- 模式的实现:
- 原型模式的克隆分为浅克隆和深克隆,Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类
//具体原型类,实现抽象原型类(Cloneable接口)
class Realizetype implements Cloneable{
Realizetype(){
System.out.println("具体原型创建成功!");
}
public Object clone() throws CloneNotSupportedException{
System.out.println("具体原型复制成功!");
return (Realizetype)super.clone();
}
}
//原型模式的测试类
public class PrototypeTest{
public static void main(String[] args) throws CloneNotSupportedException{
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype)obj1.clone();
System.out.println("obj1==obj2?" + (obj1==obj2));
}
}
3、应用场景
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 对象的创建过程比较麻烦,但复制比较简单的时候。
4、扩展
- 原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型
- 例子:用带原型管理器的原型模式来生成包含“圆”和“正方形”等图形的原型,并计算其面积。分析:本实例中由于存在不同的图形类,例如,“圆”和“正方形”,它们计算面积的方法不一样,所以需要用一个原型管理器来管理它们,图 6 所示是其结构图。
import java.util.*;
interface Shape extends Cloneable{
public Object clone(); //拷贝
public void countArea(); //计算面积
}
class Circle implements Shape{
public Object clone(){
Circle w=null;
try{
w=(Circle)super.clone();
}
catch(CloneNotSupportedException e){
System.out.println("拷贝圆失败!");
}
return w;
}
public void countArea(){
int r=0;
System.out.print("这是一个圆,请输入圆的半径:");
Scanner input=new Scanner(System.in);
r=input.nextInt();
System.out.println("该圆的面积="+3.1415*r*r+"\n");
}
}
class Square implements Shape
{
public Object clone(){
Square b=null;
try{
b=(Square)super.clone();
}
catch(CloneNotSupportedException e){
System.out.println("拷贝正方形失败!");
}
return b;
}
public void countArea(){
int a=0;
System.out.print("这是一个正方形,请输入它的边长:");
Scanner input=new Scanner(System.in);
a=input.nextInt();
System.out.println("该正方形的面积="+a*a+"\n");
}
}
class ProtoTypeManager{
private HashMap<String, Shape>ht=new HashMap<String,Shape>();
public ProtoTypeManager(){
ht.put("Circle",new Circle());
ht.put("Square",new Square());
}
public void addshape(String key,Shape obj){
ht.put(key,obj);
}
public Shape getShape(String key){
Shape temp=ht.get(key);
return (Shape) temp.clone();
}
}
public class ProtoTypeShape{
public static void main(String[] args){
ProtoTypeManager pm=new ProtoTypeManager();
Shape obj1=(Circle)pm.getShape("Circle");
obj1.countArea();
Shape obj2=(Shape)pm.getShape("Square");
obj2.countArea();
}
}
工厂方法模式
1、定义与特点
- 定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
- 如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背“开闭原则”。本节介绍的“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
- 优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
- 缺点:每增加一个新产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度
2、结构与实现
- 模式的结构:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
- 模式的实现:
//抽象产品:提供了产品的接口
interface Product{
public void show();
}
//具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product{
public void show(){
System.out.println("具体产品1显示...");
}
}
//具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product{
public void show()
{
System.out.println("具体产品2显示...");
}
}
//抽象工厂:提供了厂品的生成方法
interface AbstractFactory{
public Product newProduct();
}
//具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory{
public Product newProduct(){
System.out.println("具体工厂1生成-->具体产品1...");
return new ConcreteProduct1();
}
}
//具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory{
public Product newProduct(){
System.out.println("具体工厂2生成-->具体产品2...");
return new ConcreteProduct2();
}
}
3、应用场景
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌。
4、扩展
- 当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式
- 使用抽象工厂模式一般要满足以下条件:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
- 抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当增加一个新的产品族时不需要修改原代码,满足开闭原则。
- 缺点:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
抽象工厂模式
1、定义与特点
- 定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品
2、结构与实现
- 模式的结构:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
- 模式的实现:从图 2 可以看出抽象工厂模式的结构同工厂方法模式的结构相似,不同的是其产品的种类不止一个,所以创建产品的方法也不止一个。下面给出抽象工厂和具体工厂的代码。
//抽象工厂:提供了产品的生成方法
interface AbstractFactory{
public Product1 newProduct1();
public Product2 newProduct2();
}
//具体工厂1:实现了产品的生成方法
class ConcreteFactory1 implements AbstractFactory{
public Product1 newProduct1(){
System.out.println("具体工厂 1 生成-->具体产品 11...");
return new ConcreteProduct11();
}
public Product2 newProduct2(){
System.out.println("具体工厂 1 生成-->具体产品 21...");
return new ConcreteProduct21();
}
}
3、应用场景
- 抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如 java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的。
- 抽象工厂模式通常适用于以下场景:
-
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
4、扩展
- 抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
-
- 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
- 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则
- 当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。
建造者模式
建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
1、定义与特点
- 定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
- 优点:
- 各个具体的建造者相互独立,有利于系统的扩展。
- 客户端不必知道产品内部组成的细节,便于控制细节风险。
- 缺点:
- 产品的组成部分必须相同,这限制了其使用范围。
- 如果产品的内部变化复杂,该模式会增加很多的建造者类
2、结构与实现
- 模式的结构:
- 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。
- 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
- 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
- 模式的实现:
//产品角色:包含多个组成部件的复杂对象
class Product{
private String partA;
private String partB;
private String partC;
public void setPartA(String partA){
this.partA=partA;
}
public void setPartB(String partB){
this.partB=partB;
}
public void setPartC(String partC){
this.partC=partC;
}
//显示产品的特性
public void show(){}
}
//抽象建造者:包含创建产品各个子部件的抽象方法
abstract class Builder{
//创建产品对象
protected Product product=new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult(){
return product;
}
}
//具体建造者:实现了抽象建造者接口
public class ConcreteBuilder extends Builder{
public void buildPartA(){
product.setPartA("建造 PartA");
}
public void buildPartB(){
product.setPartA("建造 PartB");
}
public void buildPartC(){
product.setPartA("建造 PartC");
}
}
//指挥者:调用建造者中的方法完成复杂对象的创建
class Director{
private Builder builder;
public Director(Builder builder){
this.builder=builder;
}
//产品构建与组装方法
public Product construct(){
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
//客户类
public class Client{
public static void main(String[] args){
Builder builder=new ConcreteBuilder();
Director director=new Director(builder);
Product product=director.construct();
product.show();
}
}
3、应用场景
- 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
- 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的
4、扩展
- 建造者(Builder)模式在应用过程中可以根据需要改变,如果创建的产品种类只有一种,只需要一个具体建造者,这时可以省略掉抽象建造者,甚至可以省略掉指挥者角色。
更多设计模式的分享,请查看https://blog.csdn.net/pp_l_ly/category_9961646.html