设计模式学习笔记
面向对象六大设计原则
开闭原则
- Open Closed Principle
- 核心思想:对扩展开放,对修改关闭
- 也就是说,对已经使用的类的改动是通过增加代码进行的,而不是修改现有代码
单一职责原则
- Single Responsibility Principle
- 高内聚、低耦合
- 每个类只有一个职责,对外只提供一种功能,而引起变化的原因应只有一个
- 在设计模式中, 所有的设计模式都遵循这一原则
里式替换原则
- Liskov Substitution Principle
- 核心思想:在任何父类出现的地方都可以用它的子类来替换(多态)
- 也就是说,同一个继承体系中的对象应该有共同的行为特征
- 总结:只要是子类中想被其它类调用的方法,都需要在父类或者接口中出现
依赖倒转原则
- Dependency Inversion Principle
- 核心思想:要依赖抽象和接口,不要依赖于具体实现
- 其实就是说:在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类或者接口,而不是直接依赖这些其他类的具体类。
- 为了实现这一原则,就要求我们在编程的时候针对抽象类或者接口编程,而不是针对具体实现编程。
接口分离原则
- Interface Segregation Principle
- 核心思想:不应该强迫程序依赖它们不需要使用的方法
- 其实就是说:一个接口不需要提供太多行为,一个接口应该只提供一种对外的功能,不应该把所有操作都封装到一个接口中
迪米特原则
- Principle of Least Knowledge,也叫:最少认知原则
- 核心思想:一个对象应当对其他对象尽可能少的了解
- 其实就是说:降低各个对象之间的耦合,提高系统的可维护性。在模块之间应该只通过接口编程,而不理会模块的内部工作原理,它可以使各个模块耦合度降到最低,促进软件的复用
认识设计模式
设计模式概述
- 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
- 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,这些方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
- 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
- 设计模式不是一种方法和技术,而是一种思想。
- 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用。
- 学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成。
设计模式的类型
总体来说,设计模式分为三类23种:
- 创建型(5种):工厂模式(简单工厂、工厂方法)、抽象工厂模式、单例模式、原型模式、构建者模式
- 结构型(7种):适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式
- 行为型(11种):模板方法模式、策略模式、观察者模式、中介者模式、状态模式、责任链模式、命令模式、迭代器模式、访问者模式、解释器模式、备忘录模式
JavaEE设计模式:MVC、委托模式
设计模式
创建型
工厂模式
简单工厂模式
- 简单工厂模式包含3个角色:SimpleFactory(简单工厂)、AbstractProduct(抽象产品)和ConcreteProduct(具体产品),工厂拥有一个工厂方法(create),接收一个参数,通过不同的参数实例化不同的产品类。
- 图示
- 优点:很明显,“简单粗暴”,通过一个含参的工厂方法,可以实例化任何产品,上至飞机火箭、下至土豆面条,无所不能。简单工厂又名:上帝类,上帝工厂。
- 缺点:任何东西的子类都可以被生产,负担太重。当需要生产的产品种类非常多时,工厂方法的代码量可能会很庞大。在遵循开闭原则的条件下,简单工厂对于增加新产品,无能为力,因为增加新产品只能通过修改工厂方法来实现。
- 代码示例:
/**
* 抽象产品类
*/
public abstract class Product {
// 所有产品类的公共业务方法
public void methodSame(){
// 公共方法具体实现
}
// 声明抽象业务方法
public abstract void methodDiff();
}
/**
* 具体产品A
*/
public class ConcreteProductA extends Product {
@Override
public void methodDiff() {
System.out.println("ConcreteProductA");
}
}
/**
* 具体产品B
*/
public class ConcreteProductB extends Product {
@Override
public void methodDiff() {
System.out.println("ConcreteProductB");
}
}
/**
* 产品类型
*/
public class ProductTypes {
final static String TYPE_A = "A";
final static String TYPE_B = "B";
}
/**
* 普通-简单工厂
*/
public class PlainSimpleFactory {
public static Product create(String type){
Product product = null;
if(type.equalsIgnoreCase(ProductTypes.TYPE_A)){
product = new ConcreteProductA();
}else if(type.equalsIgnoreCase(ProductTypes.TYPE_B)){
product = new ConcreteProductB();
}
return product;
}
}
/**
* 静态方法-简单工厂
*/
public class StaticSimpleFactory {
public static ConcreteProductA createA(){
return new ConcreteProductA();
}
public static ConcreteProductB createB(){
return new ConcreteProductB();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Product productA1 = PlainSimpleFactory.create(ProductTypes.TYPE_A);
productA1.methodDiff();
Product productB1 = PlainSimpleFactory.create(ProductTypes.TYPE_B);
productB1.methodDiff();
Product productA2 = StaticSimpleFactory.createA();
productA2.methodDiff();
Product productB2 = StaticSimpleFactory.createB();
productB2.methodDiff();
}
}
工厂方法模式
- 工厂方法是针对每一种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品实例
- 工厂方法模式包含4个角色:AbstractProduct(抽象产品)、ConcreteProduct(具体产品)、FactoryMethod(工厂方法接口)和ConcreteFactory(具体工厂)
- 图示
- 优点:很好地减轻了工厂类的负担,把某一类/某一种东西交由一个工厂生产;同时增加某一类东西并不需要修改工厂类,只需要添加生产这类东西的工厂即可,使得工厂类符合开闭原则
- 缺点:对于某些可以形成产品族的情况处理比较复杂
- 示例代码:
/**
* 方法工厂接口
*/
public interface MethodFactory {
public Product create();
}
/**
* 具体的方法工厂A
*/
public class ConcreteFactoryA implements MethodFactory {
@Override
public Product create() {
return new ConcreteProductA();
}
}
/**
* 具体的方法工厂B
*/
public class ConcreteFactoryB implements MethodFactory {
@Override
public Product create() {
return new ConcreteProductB();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
MethodFactory factoryA = new ConcreteFactoryA();
Product productA = factoryA.create();
productA.methodDiff();
MethodFactory factoryB = new ConcreteFactoryB();
Product productB = factoryB.create();
productB.methodDiff();
}
}
抽象工厂模式
- 抽象工厂是应对产品族概念的,上边的工厂方法模式是一种极端情况下的抽象工厂模式(即只生产一种产品的抽象工厂模式),而抽象工厂模式可以看成是工厂方法模式的一种推广。
- 图示:
- 抽象工厂模式包含4个角色:AbstractProduct(抽象产品,有多个)、ConcreteProduct(具体产品)、AbstractFactory(抽象工厂接口)和ConcreteFactory(具体工厂)
- 示例代码
/**
* 抽象产品A
*/
public abstract class ProductA {
// 所有产品类的公共业务方法
public void methodSame(){
// 公共方法具体实现
}
// 声明抽象业务方法
public abstract void methodDiff();
}
/**
* 抽象产品B
*/
public abstract class ProductB {
// 所有产品类的公共业务方法
public void methodSame(){
// 公共方法具体实现
}
// 声明抽象业务方法
public abstract void methodDiff();
}
/**
* 具体产品A1
*/
public class ConcreteProductA1 extends ProductA {
@Override
public void methodDiff() {
System.out.println("ConcreteProductA1");
}
}
/**
* 具体产品A2
*/
public class ConcreteProductA2 extends ProductA {
@Override
public void methodDiff() {
System.out.println("ConcreteProductA2");
}
}
/**
* 具体产品B1
*/
public class ConcreteProductB1 extends ProductB {
@Override
public void methodDiff() {
System.out.println("ConcreteProductB1");
}
}
/**
* 具体产品B2
*/
public class ConcreteProductB2 extends ProductB {
@Override
public void methodDiff() {
System.out.println("ConcreteProductB2");
}
}
/**
* 抽象工厂
*/
public interface AbstractFactory {
ProductA createA();
ProductB createB();
}
/**
* 具体工厂1
*/
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createA() {
return new ConcreteProductA1();
}
@Override
public ProductB createB() {
return new ConcreteProductB1();
}
}
/**
* 具体工厂2
*/
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createA() {
return new ConcreteProductA2();
}
@Override
public ProductB createB() {
return new ConcreteProductB2();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
ProductA productA = factory1.createA();
productA.methodDiff();
ProductB productB = factory1.createB();
productB.methodDiff();
AbstractFactory factory2 = new ConcreteFactory2();
productA = factory2.createA();
productA.methodDiff();
productB = factory2.createB();
productB.methodDiff();
}
}
构建者模式
- 将一个复杂的对象的构造与它的表示分离,使同样的构造过程可以创建不同的表示。
- 构建者模式包含4个角色:AbstractBuilder(抽象构建者)、ConcreteBuilder(具体构建者)、Product(产品)和Director(指挥者)
- 图示:
- 示例代码
/**
* 产品类
*/
public class Product {
// 定义产品的各个部件
private String partA;
private String partB;
private String partC;
public String getPartA() {
return partA;
}
public void setPartA(String partA) {
this.partA = partA;
}
public String getPartB() {
return partB;
}
public void setPartB(String partB) {
this.partB = partB;
}
public String getPartC() {
return partC;
}
public void setPartC(String partC) {
this.partC = partC;
}
@Override
public String toString() {
return "Product [partA=" + partA + ", partB=" + partB + ", partC=" + partC + "]";
}
}
/**
* 构建者接口
*/
public interface Builder {
Builder buildPartA();
Builder buildPartB();
Builder buildPartC();
Product build();
}
/**
* 具体构建者
*/
public class ConcreteBuilder implements Builder {
private Product product = new Product();
@Override
public Builder buildPartA() {
product.setPartA("A");
return this;
}
@Override
public Builder buildPartB() {
product.setPartB("B");
return this;
}
@Override
public Builder buildPartC() {
product.setPartC("C");
return this;
}
@Override
public Product build() {
return product;
}
}
/**
* 导演类
*/
public class Director {
private Builder builder;
public Director(Builder builder){
this.builder = builder;
}
// 产品构建与组装方法
public Product constructProduct(){
return builder.buildPartA().buildPartB().buildPartC().build();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.constructProduct();
System.out.println(product); // Product [partA=A, partB=B, partC=C]
}
}
使用构建者模式的好处
- 使用构建者模式可以使客户端不必知道产品内部组成的细节
- 具体的构建者类之间是相互独立的,这有利于系统的扩展
- 具体的构建者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响
使用场景
- 创建一些复杂对象时,这些对象的内部组成部分之间的建造顺序是稳定的,但对象的内部组成构建面临着复杂的变化
- 要创建的复杂对象的算法,独立于该对象的组成部分,也独立于组成部分的装配方法时
单例模式
- 介绍:单例对象(Singleton)是一种常用的设计模式,在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例。好处是:
1)某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销
2)省去了new操作符,降低了系统内存的使用频率,减轻GC压力 - 图示
饿汉式
- 在调用获取实例方法(getInstance)之前就把对象创建好了
- 实现:
public class HungrySingleton {
// 1、构造私有
private HungrySingleton() {
}
// 2、成员变量初始化本身对象,线程安全的
private static HungrySingleton singleton = new HungrySingleton();
// 3、对外提供公共方法获取对象
public static HungrySingleton getInstance() {
return singleton;
}
}
- 测试:
public class SingletonTest {
public static void main(String[] args) {
// 饿汉式
HungrySingleton instance1 = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
if (instance1 == instance2) {
System.out.println("两个对象是相同实例");
} else {
System.out.println("两个对象是不同实例");
}
}
}
懒汉式
- 在调用获取实例方法的时候才去创建对象 – 懒加载、延迟加载
- 步骤
* 构造私有
* 定义私有静态成员变量,先不初始化
* 定义公开静态方法,获取本身对象
* 有对象就返回已有对象
* 没有对象,再去创建
- 实现:
public class LazySingleton1 {
private LazySingleton1() {}
private static LazySingleton1 instance = null;
public static LazySingleton1 getInstance() {
if (instance == null) {
instance = new LazySingleton1();
}
return instance;
}
}
- 线程安全问题,判断依据:
* 是否存在多线程
* 是否有共享数据
* 否存在非原子性操作
问题:这个类可以满足基本要求,但是,懒汉式单例实现,存在线程安全隐患,如何解决?
我们首先会想到对getSingletonInstance方法加synchronized关键字,LazySingleton2
/**
* synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降
* 因为每次调用getInstance(),都要对对象上锁
* 事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了
* 所以,这个地方需要改进
*/
public class LazySingleton2 {
private LazySingleton2() {}
private static LazySingleton2 instance = null;
public static synchronized LazySingleton2 getInstance() {
if (instance == null) {
instance = new LazySingleton2();
}
return instance;
}
}
- 双重检测锁方式实现:
/**
* 似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升
* 但是,这样的情况,还是有可能有问题的,看下面的情况:
* 在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。
* 但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。
* 这样就可能出错了,我们以A、B两个线程为例:
* a>A、B线程同时进入了第一个if判断
*
* b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
*
* c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
*
* d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
*
* e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
*
* 所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。
* 我们对该程序还得做进一步优化
*/
public class LazySingleton3 {
private LazySingleton3() {}
private static LazySingleton3 instance = null;
public static LazySingleton3 getInstance() {
if (instance == null) {
synchronized (LazySingleton3.class) {
if (instance == null) {
instance = new LazySingleton3();
// 开辟JVM堆空间 -> 产生堆内存地址保存到栈内存的Student引用中 -> 创建对象
// 存在的问题:这里是非原子性的
// 当“内存地址保存到栈内存的Student引用中”,又有一个线程进来,这个时候student != null,该线程拿到一个还没有创建的对象,会出现空指针异常
}
}
}
return instance;
}
}
- 静态内部类实现:
public class LazySingleton4 {
private LazySingleton4() {
}
/*
* 此处使用一个内部类来维护单例 JVM在类加载的时候,是互斥的,所以可以由此保证线程安全问题
*/
private static class SingletonFactory {
private static LazySingleton4 instance = new LazySingleton4();
}
public static LazySingleton4 getInstance() {
return SingletonFactory.instance;
}
}
- 测试:
public class SingletonTest {
public static void main(String[] args) {
LazySingleton4 instance3 = LazySingleton4.getInstance();
LazySingleton4 instance4 = LazySingleton4.getInstance();
if (instance3 == instance4) {
System.out.println("两个对象是相同实例"); // ^_^
} else {
System.out.println("两个对象是不同实例");
}
}
}
枚举创建
- 我们还可以使用内部静态枚举创建单例
- 实现:
public class EnumSingleton {
// 私有化构造函数
private EnumSingleton() {
}
static enum SingletonEnum {
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private EnumSingleton singleton;
// 私有化枚举的构造函数
private SingletonEnum() {
singleton = new EnumSingleton();
}
public EnumSingleton getInstance() {
return singleton;
}
}
// 对外暴露一个获取User对象的静态方法
public static EnumSingleton getInstance() {
return SingletonEnum.INSTANCE.getInstance();
}
}
原型模式
- 介绍:原型模式虽然是创建型的模式,但是与工厂模式没有关系。从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
实现Cloneable接口
/**
* 原型类
*/
public class Prototype implements Cloneable {
public Object clone() throws CloneNotSupportedException {
Prototype prototype = (Prototype) super.clone();
return prototype;
}
}
- 很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法。此处的clone方法可以改成任意名称,因为Cloneable接口是一个空接口,我们可以任意自定义实现类的方法,重点在于
super.clone()
,super.clone()
调用的是Object的clone()方法,而在Object类中clone()是native的。
通用实现
- 在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,同时将相关的参数传入新创建的对象中,保证它们的成员变量相同。
- 图示:
- 示例代码
/**
* 原型接口
*/
public interface Prototype {
Prototype clone();
}
/**
* 具体原型实现
*/
public class ConcretePrototype implements Prototype {
private String attr;
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
@Override
public String toString() {
return "ConcretePrototype [attr=" + attr + "]";
}
@Override
public Prototype clone() {
// 创建新对象
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAttr(attr);
return prototype;
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
ConcretePrototype prototype1 = new ConcretePrototype();
prototype1.setAttr("123");
ConcretePrototype prototype2 = (ConcretePrototype) prototype1.clone();
System.out.println(prototype2);
}
}
- 浅复制:将一个对象复制后,基本数据类型都会被重新创建,但是引用类型指向的还是原对象所指向的
- 深复制:将一个对象复制后,不论是基本数据类型还是引用类型,都会被重新创建。
/**
* 深浅复制
*/
public class DeepShallowCopy implements Cloneable, Serializable{
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
/**
* 浅复制
*/
public Object shallowClone() throws CloneNotSupportedException{
DeepShallowCopy proto = (DeepShallowCopy) super.clone();
return proto;
}
/**
* 深复制
* @throws ClassNotFoundException
*/
public Object deepClone() throws IOException, ClassNotFoundException{
// 写入当前对象的二进制流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 读出二进制流产生的新对象
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
class SerializableObject implements Serializable{
private static final long serialVersionUID = 1L;
}
- 测试:
public class Test {
public static void main(String[] args) throws Exception {
// 创建原对象
DeepShallowCopy prototype = new DeepShallowCopy();
prototype.setString("zhangsan");
prototype.setObj(new SerializableObject());
System.out.println("克隆之前的对象:" + prototype.getString());
System.out.println("克隆之前的对象:" + prototype.getObj());
// 浅复制出来的对象
DeepShallowCopy shallowClone = (DeepShallowCopy) prototype.shallowClone();
System.out.println("浅复制出来的对象:" + shallowClone.getString());
System.out.println("浅复制出来的对象:" + shallowClone.getObj());
// 深复制出来的对象
DeepShallowCopy deepClone = (DeepShallowCopy) prototype.deepClone();
System.out.println("深复制出来的对象:" + deepClone.getString());
System.out.println("深复制出来的对象:" + deepClone.getObj());
}
}
应用
- spring framework中BeanUtils工具类的copyProperties方法:Springframework的BeanUtils.copyProperties原理源码浅层解析
结构型
结构型模式有7种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式和享元模式,其中对象的适配模式是各种模式的起源。
适配器模式
- 介绍:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式和接口的适配器模式。
- 适配器模式包含3个角色:Target(目标抽象类)、Adapter(适配器类)和Adaptee(适配者类)
类的适配器模式
- 核心思想:有一个
Adaptee
类,拥有一个方法待适配;目标接口是Target
,通过Adapter
类,将Adaptee
类的功能扩展到Target
里 - 图示
- 示例代码
/**
* 适配者类
*/
public class Adaptee {
public void method1(){
System.out.println("This is Adaptee's method");
}
}
/**
* 目标接口
*/
public interface Target {
// 与适配者类中的方法相同
void method1();
// 新类的方法
void method2();
}
/**
* 类适配器类
*/
public class ClassAdapter extends Adaptee implements Target {
@Override
public void method2() {
System.out.println("This is Target's method");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.method1(); // This is Adaptee's method
target.method2(); // This is Target's method
}
}
这样Target
接口的实现就具有了Adaptee
类的功能
对象的适配器模式
- 基本思路和类的适配器模式相同,只是将
Adapter
类作了修改,方式是:不继承Adaptee
,而是持有Apaptee
类的实例,以达到解决兼容性的问题。 - 图示:
- 示例代码:修改适配器类即可
/**
* 对象适配器类
*/
public class ObjectAdapter implements Target{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee){
super();
this.adaptee = adaptee;
}
@Override
public void method1() {
adaptee.method1();
}
@Override
public void method2() {
System.out.println("This is Target's method");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.method1(); // This is Adaptee's method
target.method2(); // This is Target's method
}
}
接口的适配器模式
- 有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些。为了解决此问题,引入接口适配模式。借助于一个抽象类,该抽象类实现了该接口实现了所有接口,而我们不和原始接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行
- 图示
- 示例代码
/**
* 接口适配抽象类
*/
public abstract class InterfaceAdapter implements Target{
public void method1(){
System.out.println("InterfaceAdapter has implement Target's method1");
}
public void method2(){
System.out.println("InterfaceAdapter has implement Target's method2");
}
}
/**
* 目标类A
*/
public class TargetA extends InterfaceAdapter {
public void method1(){
System.out.println("This is TargetA's method1");
}
}
/**
* 目标类B
*/
public class TargetB extends InterfaceAdapter {
public void method2(){
System.out.println("This is TargetB's method2");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Target targetA = new TargetA();
Target targetB = new TargetB();
targetA.method1();
targetA.method2();
targetB.method1();
targetB.method2();
// 输出结果:
// This is TargetA's method1
// InterfaceAdapter has implement Target's method2
// InterfaceAdapter has implement Target's method1
// This is TargetB's method2
}
}
小结一下三种适配器模式的应用场景
- 类的适配模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原来的类,并实现新的接口即可。
- 对象的适配模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个类持有原对象的一个实例,并在该类的方法中调用实例方法即可。
- 接口的适配模式:当不希望实现一个接口的所有方法时,可以创建一个抽象类实现所有的方法,我们写别的类时,只需继承该抽象类即可。
装饰模式
- 介绍:装饰模式就是给一个对象增加一些新的特性,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
- 装饰模式包含4个角色:Component(抽象构件)、ConcreteComponent(具体构件)、Decorator(抽象装饰类)和ConcreteDecorator(具体装饰类)
- 图示:
- 示例代码:
/**
* 抽象构件
*/
public abstract class Component {
public abstract void operation();
}
/**
* 具体构件类
*/
public class ConcreteComponent extends Component {
@Override
public void operation() {
// 构件的基本功能
System.out.println("ConcreteComponent's basic function");
}
}
/**
* 抽象装饰类
*/
public class Decorator extends Component {
// 持有一个抽象构件对象的引用
private Component component;
// 构造函数注入一个抽象构件类型的对象
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation(); // 调用原有的业务方法
}
}
/**
* 具体的装饰类
*/
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation(){
beforeOperation();
super.operation(); // 调用原有业务方法
afterOperation();
}
// 新增业务方法
public void beforeOperation(){
System.out.println("before original operation");
}
public void afterOperation(){
System.out.println("after original operation");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecorator(component);
component.operation();
// 输出结果:
// before original operation
// ConcreteComponent's basic function
// after original operation
}
}
代理模式
- 介绍:在代理模式中引入了一样代理对象,替代原对象进行一些操作,起到中介的作用。
- 代理模式包含3个角色:Subject(抽象主题角色)、Proxy(代理主题角色)和RealSubject(真实主题角色)
- 图示:
静态代理
- 示例代码:
/**
* 抽象主题
*/
public abstract class Subject {
public abstract void request();
}
/**
* 真实主题
*/
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("RealSubject request ...");
}
}
/**
* 代理类
*/
public class Proxy extends Subject {
private RealSubject realSubject = new RealSubject();
@Override
public void request() {
preRequest();
realSubject.request();
postRequest();
}
private void preRequest(){
System.out.println("before request ...");
}
private void postRequest(){
System.out.println("after request ...");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Subject subject = new Proxy();
subject.request();
}
// 结果输出
// before request ...
// RealSubject request ...
// after request ...
}
动态代理
- JDK动态代理
public class JDKProxyFactory implements InvocationHandler {
// 目标对象的引用
private Object target;
// 通过构造函数将目标对象注入到代理对象中
public JDKProxyFactory(Object target) {
super();
this.target = target;
}
public Object getProxy(){
// 如何生成一个代理类呢?
// 1、编写源文件(java文件)----目录类接口interface实现类(调用了目标对象的方法)
// 2、编译源文件为class文件
// 3、将class文件加载到JVM中(ClassLoader)
// 4、将class文件对应的对象进行实例化(反射)
// Proxy是JDK中的API类
// 第一个参数:目标对象的类加载器
// 第二个参数:目标对象的接口
// 第三个参数:代理对象的执行处理器
Object object = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method method2 = target.getClass().getMethod("saveUser", null);
Method method3 = Class.forName("com.sun.proxy.$Proxy2").getMethod("saveUser", null);
System.out.println("目标对象的方法:" + method2.toString());
System.out.println("目标接口的方法:" + method.toString());
System.out.println("代理对象的方法:" + method3.toString());
System.out.println("这是jdk的代理方法");
// 下面的代码,是反射中的API用法
// 该行代码,实际调用的是[目标对象]的方法
// 利用反射,调用[目标对象]的方法
Object returnValue = method.invoke(target, args);
return returnValue;
}
}
- CGLIB动态代理
public class CgLibProxyFactory implements MethodInterceptor {
public Object getProxyByCgLib(Class clazz) {
// 创建增强器
Enhancer enhancer = new Enhancer();
// 设置需要增强的类的类对象
enhancer.setSuperclass(clazz);
// 设置回调函数
enhancer.setCallback(this);
// 获取增强之后的代理对象
return enhancer.create();
}
/***
* Object proxy:这是代理对象,也就是[目标对象]的子类
* Method method:[目标对象]的方法
* Object[] arg:参数
* MethodProxy methodProxy:代理对象的方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
// 因为代理对象是目标对象的子类
// 该行代码,实际调用的是父类目标对象的方法
System.out.println("这是cglib的代理方法");
// 通过调用子类[代理类]的invokeSuper方法,去实际调用[目标对象]的方法
Object returnValue = methodProxy.invokeSuper(proxy, arg);
// 代理对象调用代理对象的invokeSuper方法,而invokeSuper方法会去调用目标类的invoke方法完成目标对象的调用
return returnValue;
}
}
- 测试代码
public class TestProxy {
@Test
public void testJDKProxy() {
// 1、创建目标对象
UserService service = new UserServiceImpl();
// 2、生成代理对象
JDKProxyFactory proxyFactory = new JDKProxyFactory(service);
// 得到代理对象
UserService proxy = (UserService) proxyFactory.getProxy();
// 生成class文件
generatorClass(proxy);
// 3、调用目标对象的方法
service.saveUser();
System.out.println("===============");
// 4、调用代理对象的方法
proxy.saveUser();
}
@Test
public void testCgLibProxy() {
// 创建目标对象
UserService service = new UserServiceImpl();
// 生成代理对象
CgLibProxyFactory proxyFactory = new CgLibProxyFactory();
UserService proxy = (UserService) proxyFactory.getProxyByCgLib(service.getClass());
// 调用目标对象的方法
service.saveUser();
System.out.println("===============");
// 调用代理对象的方法
proxy.saveUser();
}
private void generatorClass(Object proxy) {
FileOutputStream out = null;
try {
// byte[] generateProxyClass =
// ProxyGenerator.generateProxyClass(proxy.getClass().getName(), new Class[]
// {proxy.getClass()});
byte[] generateProxyClass = ProxyGenerator.generateProxyClass(proxy.getClass().getSimpleName(),
new Class[] { proxy.getClass() });
out = new FileOutputStream(proxy.getClass().getSimpleName() + ".class");
out.write(generateProxyClass);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
}
}
}
}
}
外观模式
- 介绍:为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称门面模式,是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户端与子系统的耦合度。
- 外观模式包含两个角色:
(1)Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统,传递给相应的子系统对象处理
(2)SubSystem(子系统角色):在软件系统中可以有一个或多个子系统角色,每一个系统可以不是一个单独的类,而是一个类的集合,它实现了子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理外观类传来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另一个客户端而已。 - 图示:
- 示例代码:
/**
* 子系统A
*/
public class SubSystemA {
public void methodA(){
System.out.println("SubSystemA methodA");
}
}
/**
* 子系统B
*/
public class SubSystemB {
public void methodB(){
System.out.println("SubSystemB methodB");
}
}
/**
* 子系统C
*/
public class SubSystemC {
public void methodC(){
System.out.println("SubSystemC methodC");
}
}
/**
* 外观类
*/
public class Facade {
private SubSystemA subSystemA = new SubSystemA();
private SubSystemB subSystemB = new SubSystemB();
private SubSystemC subSystemC = new SubSystemC();
public void method(){
subSystemA.methodA();
subSystemB.methodB();
subSystemC.methodC();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Facade facade = new Facade();
facade.method();
}
}
桥接模式
- 介绍:将抽象部分与它的实现部分解耦,使得两者都能够独立变化。桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,是的系统更加灵活,并易于扩展,同时有效地控制了系统中类的个数。
- 桥接模式包含4个角色:
(1)Abstraction(抽象类):用于定义抽象类的接口,通常是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
(2)RefinedAbstraction(扩充抽象类):它扩充Abstraction定义的接口,通常情况下它不在是抽象类而是具体类,实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
(3)Implementor(实现类接口):定义实现类的接口,该接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同。一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作
(4)ConcreteImplementor(具体实现类):实现implementor接口并定义它的具体实现。 - 图示:
- 示例代码:
/**
* 抽象类
*/
public abstract class Abstraction {
// 定义实现类接口对象
protected Implementor impl;
public void setImpl(Implementor impl) {
this.impl = impl;
}
// 声明抽象业务方法
public abstract void operation();
}
/**
* 扩充抽象类
*/
public class RefinedAbstraction extends Abstraction {
@Override
public void operation() {
// 调用实现类的方法
impl.operationImpl();
}
}
/**
* 实现类接口
*/
public interface Implementor {
public void operationImpl();
}
/**
* 具体实现类A
*/
public class ConcreteImplementorA implements Implementor{
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operationImpl");
}
}
/**
* 具体实现类B
*/
public class ConcreteImplementorB implements Implementor{
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB operationImpl");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Abstraction absA = new RefinedAbstraction();
absA.setImpl(new ConcreteImplementorA());
absA.operation();
Abstraction absB = new RefinedAbstraction();
absB.setImpl(new ConcreteImplementorB());
absB.operation();
}
}
- 案例
- 代码:
/**
* 抽象类——人
*/
public abstract class Person {
private Clothing clothing;
private String type;
public Clothing getClothing() {
return clothing;
}
public void setClothing() {
this.clothing = ClothingFactory.getClothing();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public abstract void dress();
}
/**
* 扩充抽象类——男人
*/
public class Man extends Person {
public Man() {
setType("男人");
}
@Override
public void dress() {
Clothing clothing = getClothing();
clothing.personDressCloth(this);
}
}
/**
* 扩充抽象类——女人
*/
public class Lady extends Person {
public Lady() {
setType("女人");
}
@Override
public void dress() {
Clothing clothing = getClothing();
clothing.personDressCloth(this);
}
}
/**
* 实现类接口(抽象类)——衣服
*/
public abstract class Clothing {
public abstract void personDressCloth(Person person);
}
/**
* 具体实现类-马甲
*/
public class Jacket extends Clothing {
@Override
public void personDressCloth(Person person) {
System.out.println(person.getType() + "穿马甲");
}
}
/**
* 具体实现类——裤子
*/
public class Trouser extends Clothing {
@Override
public void personDressCloth(Person person) {
System.out.println(person.getType() + "穿裤子");
}
}
/**
* 衣服工厂
*/
public class ClothingFactory {
private static Clothing clothing;
public ClothingFactory(Clothing clothing) {
ClothingFactory.clothing = clothing;
}
public static Clothing getClothing(){
return clothing;
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
Person man = new Man();
Person lady = new Lady();
Clothing jacket = new Jacket();
Clothing trouser = new Trouser();
jacket.personDressCloth(man);
trouser.personDressCloth(man);
jacket.personDressCloth(lady);
trouser.personDressCloth(lady);
}
}
组合模式
-
介绍:
-
图示:
-
示例代码:
享元模式
-
介绍:
-
图示:
-
示例代码:
行为型
模板方法模式
- 介绍:模板方法是结构最简单的行为型设计模式,在其结构中只存在父类和子类之间的继承关系。模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。
- 模板方法模式包含2个角色:
(1)AbstractClass(抽象类):抽象类中定义一系列的基本操作,这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义实现这些步骤。同时在抽象类中实现了一个模板方法,用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
(2)ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。 - 图示:
- 示例代码:
/**
* 抽象类
*/
public abstract class AbstractClass {
// 模板方法
public void templateMethod(){
primitiveOperation1();
primitiveOperation2();
primitiveOperation3();
}
// 基本方法——具体方法
public void primitiveOperation1(){
System.out.println("AbstractClass primitiveOperation1");
}
// 基本方法——抽象方法
public abstract void primitiveOperation2();
// 基本方法——钩子方法
public void primitiveOperation3(){
System.out.println("AbstractClass primitiveOperation3");
}
}
/**
* 具体子类
*/
public class ConcreteClass extends AbstractClass {
@Override
public void primitiveOperation2() {
System.out.println("ConcreteClass implements primitiveOperation2");
}
public void primitiveOperation3() {
System.out.println("ConcreteClass primitiveOperation3");
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
AbstractClass temp = new ConcreteClass();
temp.templateMethod();
}
// 结果输出
// AbstractClass primitiveOperation1
// ConcreteClass implements primitiveOperation2
// ConcreteClass primitiveOperation3
}
- 案例:获取计算时间模板
public abstract class GetTimeTemplate {
// 固定流程方法
public long getTime() {
// 获取起始时间
long t1 = System.currentTimeMillis();
// 模板方法
code();
// 获取结束时间
long t2 = System.currentTimeMillis();
return t2 - t1;
}
// 钩子方法
public abstract void code();
}
/**
* 复制文件方法
*/
public class CopyFileDemo extends GetTimeTemplate {
@Override
public void code() {
try {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("张三.jpg"));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("mn.jpg"));
byte[] bs = new byte[256];
int len = 0;
while((len = inputStream.read(bs)) != -1){
outputStream.write(bs, 0, len);
outputStream.flush();
}
//释放资源
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* for循环计算
*/
public class ForDemo extends GetTimeTemplate{
@Override
public void code() {
//输出for循环
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
}
}
/**
* 测试
*/
public class TemplateTest {
public static void main(String[] args) {
/*GetTime time = new GetTime();
System.out.println("耗时 "+time.getTime()+" 毫秒");*/
GetTimeTemplate time = new ForDemo();
System.out.println("耗时 "+time.getTime()+" 毫秒");
GetTimeTemplate time2 = new CopyFileDemo();
System.out.println("耗时 "+time2.getTime()+" 毫秒");
}
}
策略模式
-
介绍:
-
图示:
-
示例代码:
观察者模式
-
介绍:
-
图示:
-
示例代码:
中介者模式
-
介绍:
-
图示:
-
示例代码:
状态模式
-
介绍:
-
图示:
-
示例代码:
责任链模式
-
介绍:
-
图示:
-
示例代码:
命令模式
-
介绍:
-
图示:
-
示例代码:
迭代器模式
-
介绍:
-
图示:
-
示例代码:
访问者模式
-
介绍:
-
图示:
-
示例代码:
解释器模式
-
介绍:
-
图示:
-
示例代码:
备忘录模式
-
介绍:
-
图示:
-
示例代码:
JavaEE设计模式
MVC模式
委托模式
- 委托模式或者叫委派模式,委托模式不在23种设计模式之内,但是使用这种模式的应用很多。
委托模式的表现:看似是一个对象完成的功能,但是实质上通过两个对象去实现一个功能。比如说:项目经理和开发人员的关系,客户只告诉项目经理说做什么需求,但是项目经理没有真正自己去实现该需求,是安排开发人员去实现需求。
委托模式需要的两个对象之间没有关系。 - 示例代码
/**
* 受托人:开发人员
*/
public class Developer {
public void development() {
System.out.println("又来新需求了,开干");
}
}
/**
* 委托人:项目经理
*/
public class ProjectManager {
//受托人
private Developer delegate;
public ProjectManager(Developer delegate){
this.delegate = delegate;
}
public void doSomething() {
//根据需求,将实际工作派发给开发人员或者其他人员
delegate.development();
}
}
/**
* 测试
*/
public class Test {
public static void main(String[] args) {
// 成绩是委托人的,干活的是受托人的
// 对于客户端来说,是感受不到受托人的
ProjectManager pm = new ProjectManager(new Developer());
pm.doSomething();
}
}