设计模式
1.工厂模式
-
介绍
它属于创建型模式,提供一种获取对象的最佳方式,创建对象不会对客户端暴露实现逻辑,使用一个通用接口来实现创建对象。
-
目的
定义一个创建对象接口,让子类选择实例化哪个工厂类,工厂模式使其创建过程延迟到子类。
-
优点
- 调用者只需知道类名,即可创建对象
- 扩展性高,增加一个产品即增加一个工厂类
- 屏蔽具体实现,使调用者专注于接口
-
缺点
- 每增加一个产品就要增加一个类,增加了类个数,也就增加了系统复杂度,也增加系统具体类的依赖
-
实践
- 日志记录器:记录日志存储地方,如本地系统、系统事件、远程服务器等,用户可以选择需要将日志记录位置
- 数据库访问:当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时
- 设计一个连接服务器的框架,需要三个协议,
POP3
、IMAP
、HTTP
,可以把这三个作为产品类,共同实现一个接口
-
实现
//定义接口 public interface Shape{ void draw(); } //定义两个实现类 public class Circle implements Shape{ @Override public void draw(){ sout("this is a Circle"); } } public class Rectangle implements Shape{ @Override public void draw(){ sout("this is a Rectangle"); } } //创建工厂 public class ShapeFactory{ public Shape getShape(String shapeType){ if(shapeType==null){ return null; }else if(shapeType.equalsIgnoreCase("circle")){ return new Circle(); }else if(shapeType.equalsIgnoreCase("rectangele")){ return new Rectangle(); } return null; } } //测试类 public class FactoryTest{ psvm{ //创建工厂 ShapeFactory sf = new ShapeFactory(); Shape rectangle =sf.getShape("rectangle"); rectangle.draw(); Shape circle = sf.getShape("circle"); } }
2.抽象工厂模式(abstract factory pattern)
-
介绍
也是创建者模式,提供一种创建对象方式,只不过相对于工厂模式,它是创建工厂的工厂。
-
目的
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
-
优点
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
-
缺点
产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码
-
实践
- QQ换皮肤,一整套一起换
- 生成不同操作系统的程序
-
实现
//创建一个Shape和Color接口 public interface Shape{ void draw(); } public interface Color{ void fill(); } //创建两接口实现类 public class Rectangle implements Shape{ @Override public void draw(){ sout("this is a Rectangle"); } } public class Circle implements Shape{ @Override public void draw(){ sout("this is a Circle"); } } public class Yellow implements Color{ @Override public void fill(){ sout("filled by yellow"); } } public class Blue implements Color{ @Override public void fill(){ sout("filled by blue"); } } //创建抽象工厂类 public abstract class AbstractFactory{ public abstract Color getColor(String color); public abstract Shape getShape(String shape); } //创建扩展了AbstractFactory 的工厂类,基于给定的信息生成实体类的对象 public class ShapeFactory extends AbstractFactory{ @Override public Shape getShape(String shapeType){ if(shape==null){ return null; } if(shapeType.equalsIgnoreCase("rectangle")){ return new Rectangle(); }else if(shapeType.equalsIgnoreCase("circle")){ return new Circle(); } return null; } @Override public Color getColor(String colorType){ //继承抽象类,一定要把方法继承,但是不一定要重写 return null; } } public ColorFactory extends AbstractFatory{ @Override public Shape getShape(String shapeType){ return null; } @Override public Color getColor(String colorType){ if(colorType==null){ return null; } if(colorType.equalsIngoreCase("yellow")){ return new Yellow(); }else if(colorType.equalsIgnoreCase("blue")){ return new Blue(); } return null; } } //创建一个获取工厂的工厂(工厂创造器) public class FactoryProducer{ public static AbstractFactory getFactory(String factroyType){ //获取工厂 if(factroyType.equalsIgnoreCase("ShapeFactory")){ return new ShapeFactory(); }else if(factory.equalsIgnoreCase("colorfactory")){ return new ColorFactory(); } return null; } } //使用 public class AbstractFactoryTest{ psvm{ FactoryProducer fp = new FactoryProducer(); ShapeFactory sf = fp.getFactory("shapeFactory"); ColorFactory cf = fp.getFactory("ColorFactory"); //已经得到形状和颜色工厂 Shape rectangle = sf.getShape("rectangle"); rectangle.draw(); Color yellow = cf.getColor("yellow"); yellow.fill(); } }
3.单例模式
-
介绍
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
-
目的
保证一个类仅有一个实例,并提供一个访问它的全局访问点
-
优点
- 在内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例,如首页页面缓存
- 避免对资源的多重占用(比如写文件操作)
-
缺点
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
-
实践
- Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行
- 一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件
-
应用场景
- 要求生产唯一序列号
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等
-
实现
package four; public class Singleton { private static Singleton singleton = new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return singleton; } public void showMessage(){ System.out.println("this is a singleton"); } } package four; public class SingletonTest { public static void main(String[] args) { //Singleton singleton = new Singleton();错误因为构造函数为private Singleton singleton = Singleton.getInstance();//把getInstance设置为static原因 Singleton singleton1 = Singleton.getInstance();//直接类调用 singleton.showMessage(); singleton1.showMessage(); } }
-
懒汉模式
- 简介:lazy初始化 : 是 线程安全:否 不要求线程安全,在多线程不能正常工作
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
-
懒汉模式之线程安全
-
简介:lazy初始化 : 是 线程安全:是 能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步
-
优点
第一次调用才初始化,避免内存浪费
-
缺点
必须加锁 synchronized 才能保证单例,但加锁会影响效率
-
用处
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { //就加了个synchronized if (instance == null) { instance = new Singleton(); } return instance; } }
-
-
饿汉式
-
简介:lazy初始化 : 否 线程安全:是 这种方式比较常用,但容易产生垃圾对象
-
优点
没有加锁,执行效率会提高
-
缺点
类加载时就初始化,浪费内存
-
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
-
-
双检锁
- 简介:lazy初始化 : 是 线程安全:是 这种方式采用锁机制,安全且在多线程情况下能保持高性能
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
-
登记式
-
简介:lazy初始化 : 是 线程安全:是 静态内部类实现
-
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用
-
原理
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
-
-
枚举
- 简介:lazy初始化 : 否 线程安全:是
public enum Singleton { INSTANCE; public void whateverMethod() { } }
-
总结
- 一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式