设计模式
设计模式
7个原则
-
单一职责原则:一个类负责一项职责
-
里氏替换原则:子类型(subtype)必须能够替换掉他们的基类型(base type)。
-
依赖倒置原则: 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程。
-
接口隔离原则: 建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。
-
迪米特法则: 低耦合,高内聚。
-
开闭原则: 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
创建型设计模式
单例模式(Singleton pattern)
**主要解决:**一个全局使用的类频繁地创建与销毁。
**缺点:**没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实现方式
-
饿汉式
private static Singleton uniqueInstance = new Singleton();
-
原理:依赖JVM类加载机制,保证单例只被创建1次
-
优点:线程安全;初始化速度快;占用内存小
-
缺点:单例创建时机不可控制
-
-
枚举类型
public enum Singleton{
uniqueInstance;
}
-
原理:枚举类型=不可被继承的类;每个枚举元素=类静态常量=依赖JVM类加载机制,保证单例只被创建1次;枚举元素都通过静态代码块进行初始化;构造方法访问权限默认=private;大部分方法是final
-
优点:线程安全;自由序列化;实现简单简洁
-
缺点:单例创建时机不可控制
-
-
懒汉式
public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { if(uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
-
原理:类加载时先不自动创建单例,需要时才手动创建
-
优点:按需加载单例;节约资源(延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。)
-
缺点:线程不安全
-
-
同步锁
public static synchronized Singleton getUniqueInstance() {...}
-
原理:使用同步锁
synchronized
锁住 -
优点:线程安全
-
缺点:造成过多的同步开销
-
-
双重校验锁
public class Singleton { private static Singleton uniqueInstance; private Singleton() {} public static synchronized Singleton getUniqueInstance() { if(uniqueInstance == null) { synchronized (Singleton.class) { if(uniqueTnstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }
-
原理
-
优点:线程安全
-
缺点
-
-
静态内部类
public class Singleton { private static Singleton uniqueInstance; private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getUniqueInstance() { return SingletonHolder.INSTANCE; } }
-
原理:按需加载:在静态内部类里创建单例,在装在该内部类时才会创建单例;线程安全:类由JVM加载,而JVM只会加载1遍,保证只有一个单例(当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用
getUniqueInstance()
方法从而触发SingletonHolder.INSTANCE
时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。 -
优点:线程安全
-
缺点
-
简单工厂(Simple Factory)
场景:
-
主要解决接口选择的问题。
-
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
-
数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
-
设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
-
NIO 中大量用到了工厂模式,比如
Files
类的newInputStream
方法用于创建InputStream
对象(静态工厂)、Paths
类的get
方法创建Path
对象(静态工厂)、ZipFileSystem
类(sun.nio
包下的类,属于java.nio
相关的一些内部实现)的getPath
的方法创建Path
对象(简单工厂)。
缺点:
-
工厂类集成了所有产品的创建逻辑,当工厂类出现问题,所有产品都会出现问题;
-
还有当新增加产品都会修改工厂类,违背开闭原则
实现:
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
工厂方法(Factory Method)
解决简单工厂模式的弊端,实现父类定义公共实现接口,子类负责实现创建具体的对象,这样就可以实现增加产品类时,不需要修改工厂类,而只需要修改工厂子类。
工厂方法模式的构成
-
抽象产品类:Product;描述具体产品的公共接口,在具体的产品类中实现
-
具体产品类:ProductA和ProductB;具体产品类,实现抽象产品类的接口,工厂类创建对象
-
抽象工厂类:Factory;描述具体工厂的公共接口
-
具体工厂类:FactoryA和FactoryB;描述具体工厂类,实现创建产品类对象,实现抽象工厂类的接口
代码实现:
// 抽象产品类
public abstract class Prodcut {
//抽象产品方法
public abstract sayHi();
}
// 继承抽象类实现产品A类
public class ProductA extends Product {
//实现抽象产品方法
@Overide
public abstract sayHi(){System.out.println("Hi, I'm ProductA");}
}
//继承抽象类实现产品B类
public class ProductB extends Product {
//实现抽象产品方法
@Overide
public abstract sayHi(){System.out.println("Hi, I'm ProductB");}
}
//工厂抽象类
public abstract class Factory {
//抽象工厂方法
public abstract Product createProduct();
}
//具体工厂类FactoryA
public class FactoryA extends Factory {
@Overide
public Product createProduct() {
System.out.println("生产了一个A");
return new ProductA();//生产产品A
}
}
//具体工厂类FactoryB
public class FactoryB extends Factory {
@Overide
public Product createProduct() {
System.out.println("生产了一个B");
return new ProductB();//生产产品B
}
}
-
优点:更符合开闭原则,增加一个产品类,则只需要实现其他具体的产品类和具体的工厂类即可;符合单一职责原则,每个工厂只负责生产对应的产品
-
缺点:增加一个产品,就需要实现对应的具体工厂类和具体产品类;每个产品需要有对应的具体工厂和具体产品类
抽象工厂(Abstract Factory)
系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
代码:
// 1. 为形状创建一个接口。
public interface Shape {
void draw();
}
// 2. 创建实现接口的实体类。
public class Rectangle implements Shape {
@Override
public void draw() { System.out.println("Inside Rectangle::draw() method."); }
}
public class Square implements Shape {
@Override
public void draw() { System.out.println("Inside Square::draw() method.");}
}
public class Circle implements Shape {
@Override
public void draw() { System.out.println("Inside Circle::draw() method.");}
}
// 3. 为颜色创建一个接口。
public interface Color {
void fill();
}
// 4. 创建实现颜色接口的实体类。
public class Red implements Color {
@Override
public void fill() { System.out.println("Inside Red::fill() method.");}
}
public class Green implements Color {
@Override
public void fill() { System.out.println("Inside Green::fill() method.");}
}
public class Blue implements Color {
@Override
public void fill() { System.out.println("Inside Blue::fill() method.");}
}
// 5. 为 Color 和 Shape 对象创建抽象类来获取工厂。
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape);
}
// 6. 创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); }
else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle();}
if(shapeType.equalsIgnoreCase("SQUARE")){ return new Square();}
return null;
}
@Override
public Color getColor(String color) { return null; }
}
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){ return null; }
@Override
public Color getColor(String color) {
if(color == null){ return null; }
if(color.equalsIgnoreCase("RED")){ return new Red(); }
else if(color.equalsIgnoreCase("GREEN")){ return new Green(); }
else if(color.equalsIgnoreCase("BLUE")){ return new Blue(); }
return null;
}
}
// 7. 创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
// 8. 使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象。
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//获取形状为 Circle 的对象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取形状为 Rectangle 的对象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取形状为 Square 的对象
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
//获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//获取颜色为 Red 的对象
Color color1 = colorFactory.getColor("RED");
//调用 Red 的 fill 方法
color1.fill();
//获取颜色为 Green 的对象
Color color2 = colorFactory.getColor("GREEN");
//调用 Green 的 fill 方法
color2.fill();
//获取颜色为 Blue 的对象
Color color3 = colorFactory.getColor("BLUE");
//调用 Blue 的 fill 方法
color3.fill();
}
}
生成器(Builder)
原型模式(Prototype)
结构型设计模式
外观模式(Facade)
适配器模式(Adapter)
主要用于接口互不兼容的类的协调工作
适配者(Adaptee) :适配器模式中存在被适配的对象或者类
适配器(Adapter) :作用于适配者的对象或者类
适配器分为对象适配器和类适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
适配器和适配者两者不需要继承相同的抽象类或者实现相同的接口。
自己总结:原先系统的类A, 需要接入系统的新类B,B的接口与我们系统的接口不兼容且不能修改B.
构建适配器Adapter:1.实现接口A -> AImp, 同时实现接口B ->BImp;2.AImp中使用接口B的引用,重载方法中调用B的方法。3.使用时:
A a = new AImp( new BImp() );
应用 1:
-
IO 流中的字符流和字节流的接口不同,它们之间可以协调工作就是基于适配器模式来做的
-
InputStreamReader
和OutputStreamWriter
就是两个适配器(Adapter) -
InputStreamReader
使用StreamDecoder
(流解码器)对字节进行解码,实现字节流到字符流的转换, -
OutputStreamWriter
使用StreamEncoder
(流编码器)对字符进行编码,实现字符流到字节流的转换。 -
InputStream
和OutputStream
的子类是被适配者,InputStreamReader
和OutputStreamWriter
是适配器。
应用 2 :
-
FutureTask
类使用了适配器模式,Executors
的内部类RunnableAdapter
实现属于适配器,用于将Runnable
适配成Callable
。
应用 3 (例子)
// 1. 确定目标接口: 系统原来的日志接口如下
public interface LogFactory {
void debug(String tag,String message);
}
// 2. 第三方库提供的日志功能,其接口与系统目前使用的不兼容。
public interface NbLogger {
void d(int priority, String message, Object ... obj);
}
//具体提供日志功能的实现类
public class NbLoggerImp implements NbLogger {
@Override
public void d(int priority, String message, Object... obj) {
System.out.println(String.format("牛逼logger记录:%s",message));
}
}
// 3. 构建适配器类, 通过此类就可以将三方库提供的接口转换为系统的目标接口
public class LogAdapter implements LogFactory {
private NbLogger nbLogger;
public LogAdapter(NbLogger nbLogger) {
this.bnLogger = nbLogger;
}
@Override
public void debug(String tag, String message) {
Objects.requireNonNull(nbLogger);
nbLogger.d(1, message);
}
}
// !!! LogAdapter实现了系统的目标接口,同时持有三方库NbLogger的引用。
// 4. 客户端使用
public class AdapterClient {
public void recordLog() {
LogFactory logFactory = new LogAdapter(new NbLoggerImp());
logFactory.debug("Test", "我将使用牛逼logger打印log");
}
}
桥接模式(Bridge pattern)
组合模式(composite pattern)
装饰者模式(decorator pattern)
-
动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。
-
可以在不改变原有对象的情况下拓展其功能。
-
应用场景:
-
InputStream 的子类 FilterInputStream
-
OutputStream 的子类 FilterOutputStream
-
Reader 的子类 BufferedReader 以及 FilterReader
-
Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。
-
实现(例子:创建一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。):
-
创建
Shape
接口 -
创建实现接口的实体类:
Rectangle、Circle
-
创建实现了
Shape
接口的抽象装饰类:ShapeDecorator implements Shape
-
创建扩展了
ShapeDecorator
类的实体装饰类:RedShapeDecorator extends ShapeDecorator
-
使用
RedShapeDecorator
来装饰Shape
对象。
// 1. 创建一个接口
public interface Shape {
void draw();
}
// 2. 创建实现接口的实体类。
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
// 3. 创建实现了 Shape 接口的抽象装饰类。
public abstract class ShapeDecorator implements Shape{
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
public void draw() {
decoratedShape.draw();
}
}
// 4. 创建扩展了 ShapeDecorator 类的实体装饰类。
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
}
// 5. 使用 RedShapeDecorator 来装饰 Shape 对象。
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
circle.draw();
redCircle.draw();
redRectangle.draw();
}
}
享元模式(Flyweight Pattern):
代理模式(Proxy pattern)
行为型设计模式
责任链模式(Chain of responsibility pattern)
策略模式(strategy pattern)
模板方法模式(Template pattern)
命令模式(Command pattern)
观察者模式(observer pattern)
它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。
缺点:
-
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
-
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
-
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
场景:
-
NIO 中的文件目录监听服务使用到了观察者模式。
实现:
// 1. 创建Subject类
public class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {return state;}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){ observers.add(observer); }
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
// 2. 创建 Observer 类。
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
// 3. 创建实体观察者类。
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
public class OctalObserver extends Observer{
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
}
}
public class HexaObserver extends Observer{
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
// 4. 使用 Subject 和实体观察者对象。
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
访问者模式(visitor pattern)
状态模式(State pattern)
解释器模式(Interpreter pattern)
迭代器模式(iterator pattern)
中介者模式(Mediator pattern)
备忘录模式(Memento pattern)