一、定义
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
二、七大原则
2.1、单一职责原则
对类来说的,既一个类应该只负责一项职责。如类A负责两个不同的职责:职责1、职责2。当职责1需求变更而改变类A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、A2。
2.2、接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
2.3、依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖其抽象。依赖倒置的中心思想是面向接口编程。
2.4、里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象(继承必须确保基类所拥有的性质在子类中仍然成立)(子类尽可能不要重写父类的方法)。
2.5、开闭原则
对扩展开放,对修改关闭。
2.6、迪米特法则
一个类对自己依赖的类知道的越少越好。对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了public方法,不对外泄露任何信息。核心是降低类之间的耦合。
2.7、合成复用原则
尽量使用合成//聚合的方式,而不是使用继承。
三、UML
UML(Unified Modeling Language)统一建模语言,是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果。
UML本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素之间的关系,比如类、接口、实现、泛化(继承)、依赖、组合、聚合等。
四、设计模式分类
创建型模式:单例模式、工厂方法模式、抽象工厂模式、原型模式、建造者模式
结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式
五、设计模式
5.1、单例模式
5.1.1、介绍
保证某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
5.1.2、代码实现
1)、静态常量饿汉式
// 单例模式-饿汉式-静态常量
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
// 本类内部创建对象实例,类加载时就完成实例化
private final static Singleton instance = new Singleton();
// 提供一个公有静态的方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
优点:写法简单,类加载的时候就完成实例化,避免了线程同步问题
缺点:没有达到懒加载的效果,可能造成内存浪费
2)、静态代码块饿汉式
// 单例模式-饿汉式-静态代码块
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
private final static Singleton instance;
// 静态代码块中创建对象实例,类加载时就完成实例化
static {
instance = new Singleton();
}
// 提供一个公有静态的方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
优点:类加载的时候就完成实例化,避免了线程同步问题
缺点:没有达到懒加载的效果,可能造成内存浪费
3)、线程不安全懒汉式
// 单例模式-懒汉式-线程不安全
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
private static Singleton instance;
// 提供一个公有静态的方法,返回实例对象
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:达到懒加载的效果,但是只能单线程下使用
缺点:线程不安全,实际开发中不要使用
4)、线程安全懒汉式
// 单例模式-懒汉式-线程安全
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
private static Singleton instance;
// 加入同步代码,保证线程安全
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:线程安全
缺点:效率低,实际开发中不推荐
5)、同步代码块懒汉式
// 单例模式-懒汉式-同步代码块-线程不安全
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
// 同步代码块
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
缺点:线程不安全,效率也不高,实际开发中不能使用
6)、双重检查懒汉式
// 单例模式-懒汉式-双重检查-线程安全
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
private static Singleton instance;
public static Singleton getInstance() {
// 第一次检查
if (instance == null) {
// 同步代码块,保证线程安全
synchronized (Singleton.class) {
// 第二次检查
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优点:线程安全、延迟加载、效率较高,实际开发中推荐使用
7)、静态内部类懒汉式
// 单例模式-懒汉式-静态内部类-线程安全
public class Singleton {
// 构造器私有化,外部不能new
private Singleton() {}
// 外部类加载时,静态内部类不会加载,保证懒加载效果
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
// 类加载,保证线程安全
return SingletonInstance.INSTANCE;
}
}
优点:通过类加载机制保证线程安全,利用静态内部类特点实现延迟加载,效率高,推荐使用
8)、枚举
// 单例模式-枚举-线程安全
enum Singleton {
INSTANCE;
}
优点:线程安全,还能防止反序列化重新创建对象,推荐使用
5.1.3、例子
jdk中的Runtime类中使用了静态变量饿汉式的单例模式
5.1.4、使用场景
频繁创建和销毁的对象,创建对象时耗时过多或者耗费资源过多,但是又是经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
5.2、简单工厂模式
5.2.1、介绍
通过一个工厂类来集中管理对象的创建逻辑,客户端无需直接实例化具体类,实现对象创建与使用的解耦。简单工厂模式是工厂模式的一种。
5.2.2、代码实现
// 披萨 抽象类
public abstract class Pizza {
protected String name;
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
public void setName(String name) {
this.name = name;
}
}
// 希腊披萨类,继承披萨类
public class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("希腊披萨准备原材料");
}
}
// 奶酪披萨类,继承披萨类
public class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("奶酪披萨准备原材料");
}
}
// 简单工厂类
public class SimpleFactory {
// 提供创建披萨方法
public Pizza createPizza(String type) {
System.out.println("使用简单工厂模式");
Pizza pizza = null;
if ("greek".equals(type)) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if ("cheese".equals(type)) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}
return pizza;
}
// 提供创建披萨方法-静态方法
public static Pizza createPizza2(String type) {
System.out.println("使用简单工厂模式-静态方法");
Pizza pizza = null;
if ("greek".equals(type)) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if ("cheese".equals(type)) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
}
return pizza;
}
}
// 披萨店类
public class PizzaStore {
// 提供披萨下单方法
public Pizza orderPizza(String type) {
SimpleFactory simpleFactory = new SimpleFactory();
Pizza pizza = simpleFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 提供披萨下单方法
public Pizza orderPizza2(String type) {
Pizza pizza = SimpleFactory.createPizza2(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
store.orderPizza("greek");
store.orderPizza2("cheese");
}
}
5.2.3、例子
Calendar类(时间操作类)
5.3、工厂方法模式
5.3.1、介绍
定义了一个创建对象的抽象方法,由子类决定要实例化的类。将对象的实例化推迟到子类。每个子类都可以实现工厂接口以提供具体的对象实例化过程
5.3.2、代码实现
// 抽象类
public abstract class OrderPizza {
// 抽象方法,让各个子类工厂区实现具体业务
public abstract Pizza createPizza();
public OrderPizza() {
Pizza pizza = createPizza();
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
}
public class GreekOrderPizza extends OrderPizza {
@Override
public Pizza createPizza() {
Pizza pizza = new GreekPizza();
pizza.setName("希腊披萨");
return pizza;
}
}
public class CheeseOrderPizza extends OrderPizza {
@Override
public Pizza createPizza() {
Pizza pizza = new CheesePizza();
pizza.setName("奶酪披萨");
return pizza;
}
}
// 披萨店类
public class PizzaStore {
public static void main(String[] args) {
new CheeseOrderPizza();
new GreekOrderPizza();
}
}
5.4、抽象工厂模式
5.4.1、介绍
提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式允许客户端在不指定具体类的情况下,创建一系列的产品对象。
我的理解:大体上跟工厂方法模式差不多,多了一些抽象方法来创建多种对象。
5.5、原型模式
5.5.1、介绍
通过复制(克隆)现有对象来创建新对象,而不是通过使用构造函数创建。原型模式的核心思想是基于现有对象创建新的对象,而不是从零开始创建。
5.5.2、代码实现
1)、浅拷贝
public class Sheep implements Serializable, Cloneable {
private String name;
private String color;
public Sheep(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" + "name='" + name + '\'' + ", color='" + color + '\'' + '}';
}
// 克隆
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sheep;
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("喜洋洋", "白色");
Sheep sheep1 = (Sheep) sheep.clone();// 克隆
Sheep sheep2 = (Sheep) sheep.clone();// 克隆
System.out.println(sheep.toString());
System.out.println(sheep1.toString());
System.out.println(sheep2.toString());
}
}
2)、深拷贝(clone方法、反序列化)
public class DeepSheep implements Serializable, Cloneable {
private String name;
private Sheep sheep;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Sheep getSheep() {
return sheep;
}
public void setSheep(Sheep sheep) {
this.sheep = sheep;
}
@Override
public String toString() {
return "DeepSheep{" + "name='" + name + '\'' + ", sheep=" + sheep + '}';
}
// 方式1:clone
@Override
protected Object clone() {
Object deep = null;
try {
// 完成基本类型克隆
deep = super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
// 引用类型克隆
DeepSheep deepSheep = (DeepSheep) deep;
deepSheep.sheep = (Sheep) sheep.clone();
return deepSheep;
}
// 方式2:反序列化
public Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepSheep deepSheep = (DeepSheep) ois.readObject();
return deepSheep;
} catch (Exception e) {
} finally {
try {
if (ois != null) {
ois.close();
}
if (bis != null) {
bis.close();
}
if (oos != null) {
oos.close();
}
if (bos != null) {
bos.close();
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
return null;
}
}
public class Client {
public static void main(String[] args) {
DeepSheep deepSheep = new DeepSheep();
deepSheep.setName("懒洋洋");
Sheep sheep = new Sheep("喜洋洋", "白色");
deepSheep.setSheep(sheep);
// 方式1
DeepSheep deepSheep1 = (DeepSheep) deepSheep.clone();
System.out.println(deepSheep.toString());
System.out.println(deepSheep1.toString());
System.out.println(deepSheep.getSheep() == deepSheep1.getSheep());
// 方式2
DeepSheep deepSheep2 = (DeepSheep) deepSheep.deepClone();
System.out.println(deepSheep.toString());
System.out.println(deepSheep2.toString());
System.out.println(deepSheep.getSheep() == deepSheep2.getSheep());
}
}
5.5.3、例子
Spring中配置bean使用到scope="prototype"配置项,创建bean时会使用原型模式。
5.6、建造者模式
5.6.1、介绍
建造者模式又叫生成器模式,是一种对象构建模式。
它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
建造者模式包含以下角色:
Product(产品角色): 要创建的复杂对象。
Builder(抽象建造者):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
ConcreteBuilder(具体建造者):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
Director(指挥者): 调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
5.6.2、代码实现
// 产品 Product
public class House {
private String base;
private String wall;
private String roof;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoof() {
return roof;
}
public void setRoof(String roof) {
this.roof = roof;
}
}
// 抽象建造者 Builder
public abstract class HouseBuilder {
protected House house = new House();
// 建造流程
public abstract void buildBase();
public abstract void buildWall();
public abstract void buildRoof();
public House buildHouse() {
return house;
}
}
// 具体建造者 ConcreteBuilder
public class CommonHouse extends HouseBuilder {
@Override
public void buildBase() {
System.out.println("普通房子打地基");
}
@Override
public void buildWall() {
System.out.println("普通房子砌墙");
}
@Override
public void buildRoof() {
System.out.println("普通房子屋顶");
}
}
// 指挥者 Director
public class HouseDirector {
HouseBuilder houseBuilder = null;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
// 建造流程
public House constructHouse() {
houseBuilder.buildBase();
houseBuilder.buildWall();
houseBuilder.buildRoof();
return houseBuilder.house;
}
}
public class Client {
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
HouseDirector houseDirector = new HouseDirector(commonHouse);
House house = houseDirector.constructHouse();
}
}
5.7、适配器模式
5.7.1、介绍
适配器模式是将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容。使得原本不兼容的类能够协同工作。
5.7.2、代码实现
1)、类适配器
Adapter类通过继承src类,实现dst类接口,完成src->dst的适配。
// 被适配者 src
public class Voltage220V {
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "V");
return src;
}
}
// 目标 dst
public interface Voltage5V {
public int output5V();
}
// 适配器
public class VoltageAdapter extends Voltage220V implements Voltage5V{
@Override
public int output5V() {
int srcV = output220V();
int dstV = srcV / 44; // 将220V转成5V
return dstV;
}
}
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("充电成功");
} else {
System.out.println("充电失败");
}
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
2)、对象适配器
Adapter类不再继承src类,而是持有src类的实例。
// 适配器不使用继承被适配器类
public class VoltageAdapter2 implements Voltage5V{
private Voltage220V voltage220V;// 关联关系-聚合
public VoltageAdapter2(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dstV = 0;
if (voltage220V != null) {
int srcV = voltage220V.output220V();
dstV = srcV / 44; // 转成5V
}
return dstV;
}
}
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("充电成功");
} else {
System.out.println("充电失败");
}
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter2(new Voltage220V()));
}
}
3)、接口适配器
当不需要全部实现接口提供的方法时,可设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以有选择地覆盖父类的某些方法来实现需求。
public interface Interface3 {
public void operation1();
public void operation2();
public void operation3();
public void operation4();
}
// 默认实现Interface3的方法
public class AbsAdapter implements Interface3{
@Override
public void operation1() {}
@Override
public void operation2() {}
@Override
public void operation3() {}
@Override
public void operation4() {}
}
public class Client {
public static void main(String[] args) {
AbsAdapter adapter = new AbsAdapter() {
// 只需要去覆盖我们需要的接口方法
@Override
public void operation1() {
System.out.println("使用operation1方法");
}
};
adapter.operation1();
}
}
5.8、桥接模式
5.8.1、介绍
将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。桥接模式基于类的最小设计原则,通过使用封装、聚合级继承等行为让不同的类承担不同的职责。它的主要特点是把抽象与行为实现分离开来,从而可以保持各部分的独立性以及应对他们功能的扩展。
5.8.2、代码实现
// 接口
public interface Brand {
public void open();
public void close();
public void call();
}
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米手机-开机");
}
@Override
public void close() {
System.out.println("小米手机-关机");
}
@Override
public void call() {
System.out.println("小米手机-打电话");
}
}
public class Vivo implements Brand{
@Override
public void open() {
System.out.println("Vivo手机-开机");
}
@Override
public void close() {
System.out.println("Vivo手机-关机");
}
@Override
public void call() {
System.out.println("Vivo手机-打电话");
}
}
public abstract class Phone {
private Brand brand;
public Phone(Brand brand) {
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
public class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println("折叠手机");
}
public void close() {
super.close();
System.out.println("折叠手机");
}
public void call() {
super.call();
System.out.println("折叠手机");
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new FoldedPhone(new XiaoMi());
phone.open();
phone.close();
phone.call();
System.out.println("-------------------------");
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.close();
phone2.call();
}
}
5.9、装饰者模式
5.9.1、介绍
动态地将新功能附加到对象上。在对象的功能扩展方面,它比继承更有弹性。
5.9.2、代码实现
public abstract class Drink {
private String des;
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
// 计算费用 子类实现
public abstract float cost();
}
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
public class EspressoCoffee extends Coffee {
public EspressoCoffee() {
setDes("意式浓缩咖啡");
setPrice(6.0f);
}
}
public class AmericanoCoffee extends Coffee {
public AmericanoCoffee() {
setDes("美式咖啡");
setPrice(5.0f);
}
}
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
return super.getPrice() + drink.cost();
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDes("巧克力");
setPrice(3.3f);
}
}
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
setDes("牛奶");
setPrice(2.3f);
}
}
public class Client {
public static void main(String[] args) {
Drink order = new AmericanoCoffee();
System.out.println("美式咖啡 * 1,价格" + order.cost());
order = new Milk(order);
System.out.println("美式咖啡 * 1 + 牛奶 * 1,价格" + order.cost());
order = new Chocolate(order);
System.out.println("美式咖啡 * 1 + 牛奶 * 1 + 巧克力 * 1,价格" + order.cost());
}
}
5.9.3、例子
java的IO结构中,FilterInputStream就是一个装饰者
5.10、组合模式
5.10.1、介绍
组合模式又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树形结构以表示“整体-部分”的层次关系。
5.10.2、代码实现
public abstract class Organ {
private String name;
public Organ(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected void add(Organ organ) {
throw new UnsupportedOperationException();
}
protected void remove(Organ organ) {
throw new UnsupportedOperationException();
}
protected abstract void print();
}
public class University extends Organ {
List<Organ> organs = new ArrayList<>();
public University(String name) {
super(name);
}
@Override
protected void add(Organ organ) {
organs.add(organ);
}
@Override
protected void remove(Organ organ) {
organs.remove(organ);
}
@Override
protected void print() {
System.out.println(super.getName());
for (Organ o : organs) {
o.print();
}
}
}
public class College extends Organ {
List<Organ> organs = new ArrayList<>();
public College(String name) {
super(name);
}
@Override
protected void add(Organ organ) {
organs.add(organ);
}
@Override
protected void remove(Organ organ) {
organs.remove(organ);
}
@Override
protected void print() {
System.out.println(super.getName());
for (Organ o : organs) {
o.print();
}
}
}
public class Department extends Organ {
public Department(String name) {
super(name);
}
@Override
protected void print() {
System.out.println(super.getName());
}
}
public class Client {
public static void main(String[] args) {
Organ university = new University("清华大学");
Organ c1 = new College("计算机学院");
Organ c2 = new College("信息工程学院");
c1.add(new Department("计算机科学与技术"));
c1.add(new Department("软件工程"));
c2.add(new Department("信息工程"));
c2.add(new Department("通信工程"));
university.add(c1);
university.add(c2);
university.print();
c1.print();
c2.print();
}
}
5.11、外观模式
5.11.1、介绍
外观模式也叫过程模式,通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心子系统的内部细节。
5.11.2、代码实现
public class DVD {
private static DVD instance = new DVD();
public static DVD getInstance() {
return instance;
}
public void on() {
System.out.println("DVD播放");
}
public void off() {
System.out.println("DVD关闭");
}
}
public class Light {
private static Light instance = new Light();
public static Light getInstance() {
return instance;
}
public void on() {
System.out.println("电灯打开");
}
public void off() {
System.out.println("电灯关闭");
}
}
public class HomeFacade {
private DVD dvd;
private Light light;
public HomeFacade() {
super();
this.dvd = DVD.getInstance();
this.light = Light.getInstance();
}
public void ready() {
System.out.println("准备看DVD");
dvd.on();
light.off();
}
public void end() {
System.out.println("看完DVD了");
dvd.off();
light.on();
}
}
public class Client {
public static void main(String[] args) {
HomeFacade homeFacade = new HomeFacade();
homeFacade.ready();
homeFacade.end();
}
}
5.12、享元模式
5.12.1、介绍
享元模式也叫蝇量模式,运用共享技术有效地支持大量细粒度的请求。享元模式经典应用场景就是池技术,String常量池、数据库连接池、缓冲池等都是享元模式的应用。
5.12.2、代码实现
public abstract class WebSite {
public abstract void use();
}
public class ConcreateWebSite extends WebSite {
private String type = "";
public ConcreateWebSite(String type) {
this.type = type;
}
@Override
public void use() {
System.out.println("网站发布形式:" + type);
}
}
public class WebSiteFactory {
// 充当池的作用
private HashMap<String, ConcreateWebSite> pool = new HashMap<>();
public WebSite getWebSite(String type) {
if (!pool.containsKey(type)) {
pool.put(type, new ConcreateWebSite(type));
}
return (WebSite) pool.get(type);
}
public int getWebSiteCount() {
return pool.size();
}
}
public class Client {
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
WebSite webSite = factory.getWebSite("新闻");
webSite.use();
WebSite webSite2 = factory.getWebSite("博客");
webSite2.use();
WebSite webSite3 = factory.getWebSite("博客");
webSite3.use();
System.out.println(factory.getWebSiteCount());
}
}
5.12.3、例子
Integer中valueOf方法使用了享元模式。-128到127范围内的值都缓存起来了。
5.13、代理模式
5.13.1、介绍
为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
代理模式有不同的形式,主要有两种:静态代理、动态代理(JDK代理、Cglib代理)。
5.13.2、代码实现
1)、静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(目标对象)与代理对象一起实现相同的接口或者继承相同的类。
public interface ITeacher {
void teach();
}
// 目标对象
public class Teacher implements ITeacher{
@Override
public void teach() {
System.out.println("老师教书");
}
}
// 代理对象
public class TeacherProxy implements ITeacher{
private ITeacher target;// 目标对象 通过接口聚合
public TeacherProxy(ITeacher target) {
super();
this.target = target;
}
@Override
public void teach() {
System.out.println("开始代理");
target.teach();
System.out.println("结束代理");
}
}
public class Client {
public static void main(String[] args) {
// 创建目标对象
Teacher teacher = new Teacher();
// 创建代理对象,将目标对象传递给代理对象
TeacherProxy proxy = new TeacherProxy(teacher);
proxy.teach();
}
}
优点:在不修改目标对象的功能前提下,通过代理对象对目标功能扩展
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。
一旦接口增加方法,目标对象与代理对象都要维护
2)、动态代理(JDK代理)
代理对象不需要实现接口,但是目标对象需要实现接口,否则不能使用动态代理。
代理对象的生成是利用JDK的API,动态在内存中构建代理对象。
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// public static Object newProxyInstance(ClassLoader loader,
// Class<?>[] interfaces,
// InvocationHandler h)
// ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
// Class<?>[] interfaces:目标对象的实现接口类型,使用泛型方法确认类型
// InvocationHandler h:事情处理,执行目标方法时,会触发事件处理器,会把当前执行的目标对象方法作为参数传入
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
Object returnVal = method.invoke(target, args);
System.out.println("JDK代理结束");
return returnVal;
}
}
);
}
}
public class Client {
public static void main(String[] args) {
// 创建目标对象
Teacher teacher = new Teacher();
// 创建代理对象
ITeacher proxy = (ITeacher) new ProxyFactory(teacher).getProxyInstance();
proxy.teach();
}
}
3)、动态代理(Cglib代理)
目标对象没有实现任何接口,可通过目标对象的子类来实现代理。
// 目标对象 不需要实现接口
public class Teacher {
public void teach() {
System.out.println("老师教书");
}
}
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
super();
this.target = target;
}
public Object getProxyInstance() {
// 创建一个工具类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类对象,即代理对象
return enhancer.create();
}
// 重写intercept方法,会调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib开始代理");
Object returnVal = method.invoke(target, objects);
System.out.println("Cglib结束代理");
return returnVal;
}
}
public class Client {
public static void main(String[] args) {
// 创建目标对象
Teacher teacher = new Teacher();
// 创建代理对象
Teacher proxy = (Teacher) new ProxyFactory(teacher).getProxyInstance();
proxy.teach();
}
}