目录标题
- OO设计原则
- 策略模式 - Strategy
- 定义
- 案例分析
- 需求
- 思路分析
- 核心代码展示
- 进一步优化
- UML 图
- 观察者模式 - Observe
- 定义
- 案例分析
- 需求
- UML图
- 内置的Java观察者模式
- 核心代码
- 总结
- 装饰者模式 - Decorator
- 定义
- 案例分析
- 需求
- UML图分析
- 核心代码
- 总结
- 工厂模式 - Abstract Method/Factory Method
- 定义
- 案例分析
- 简易工厂模式
- 需求
- UML图
- 核心代码
- 抽象工厂模式
- 需求
- UML图
- 核心代码
- 总结
- 单例模式 - Singleton
- 双重检验锁
- 热实例化
- 同步语句块
- 命令模式 - Command
- 定义
- 案例分析
- 需求
- 分析
- 核心代码
- 总结
- 适配器模式 - Adapter
- 定义
- 需求
- 核心代码
- 外观模式 - Facade
- 定义
- 核心代码
- 模板方法模式 - Template Method
- 定义
- 核心代码
- 迭代器模式 - Iterator
- 定义
- 核心代码
- 组合模式 - Composite
- 定义
- 核心代码
- 状态模式 - State
- 定义
- 核心代码
- 代理模式 - Proxy
- 定义
- 需求
- 核心代码
- 总结
- 思维导图
- 模式的分类
- 根据模式的目标分类
- 创建型
- 行为型
- 结构型
- 模式所处理的对象
- 类
- 对象
代码仓库:hanliy/Head-First-DesignPatterns-Demos
书籍推荐:Head First设计模式(中文版)、 广受推荐的最新设计模式书籍:《深入设计模式》(需购买)
进阶推荐:Java Design Patterns
前言
这本书《Head First 设计模式(中文版)》看了一个月左右(2024年3月4号 ~ 2024年4月8号),采用大量的插图、图例来进行辅助讲解,插图设计的非常和内容贴切。入门级别,强烈推荐。
看完了以后,也写了一个小小的实战 【设计模式】实战篇,算是对自己这一个月的小总结。
等把这个 【设计模式】实战篇 写完,算是对自己前一个月的收尾。Orz,希望不会咕咕咕~
OO设计原则
- 封装变化
- 找出应用中可能需要变化之处,把他们独立出来,不要和哪些不需要变化的代码混在一起
- 分开变化和不会变化的部分:
- 把会变化的部门取出来并封装起来,以便以后可以轻易的改动和扩充此部分,而不影响不需要变化的部分
- 多使用组合,少用继承
- 针对接口编程,而不是针对实现类
Before :子类的行为来自于继承某个接口的自行实现 —> 过于绑定了
After:子类使用接口表示行为,实际的 “实现” 不会绑定在子类中,特定的行为只在接口中的实现了中存在
- 为了交互对象之间的松耦合设计而努力。
- 开闭原则:类应该对扩展开放,对修改关闭
添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)
- 依赖倒置原则:依赖抽象,不要依赖具体的类
- 最少知识原则:减少对象之间的交互,只留下几个“密友”
- **好莱坞原则:“Don’t call me; I’ll call you.” **
好莱坞原则是用在创建框架或组件上的一种技巧,好让低层组件能够被挂钩进计算中,而且又不会让高层组件依赖低层组件。
- 单一原则:一个类应该只有一个引起变化的原因
好莱坞原则和依赖倒置原则之间的关系如何?
两者的目标都是在于解耦。
但是依赖倒置原则更加注重如何在设计中避免依赖。
好莱坞原则教我们一个技巧,创建一个有弹性的设计,允许低层结构能够互相操作,而又防止其他类太过依赖它们。
策略模式 - Strategy
定义
封装可互换的行为,然后使用委托来决定要采用哪一个行为
策略模式中,所组合的类实现了整个算法。
定义一个算法家族,并让这些算法可以互换。正因为每一个法都被封装起来了,所以客户可以轻易地使用不同的算法。
案例分析
需求
假定需要创建出很多个鸭子:绿头鸭、橡皮鸭、木头鸭等等。如何进行设计呢?
思路分析
设计分析
- 新建个
Duck
鸭子类,设为抽象类(提高代码的复用),具有 统一 的一些行为:display
外观(设计为抽象方法)、swim
游泳(默认的实现方法) - 考虑到
Duck
鸭子类, 有一些 变化 的地方:Fly飞翔、 Quack呱呱叫
FlyBehavior
接口, 不同的实现类实现接口中对fly
飞翔行为的实现:FlyNoWay
、FlyWithWings
QuackBehavior
接口, 不同的实现类实现接口中对quack
呱呱叫行为的实现:MuteQuack
、Quack
、Squeak
Duck
鸭子类 加入两个实例变量flyBehavior
、quackBehavior
,声明为接口类型,这样每个对象都会在运行的时候动态的引用正确的行为
具体实现
这样,假定我们需要 MallardDuck
绿头鸭类,那么在构建的时候,使用多态指定 FlyBehavior
接口 和 QuackBehavior
接口 的实现类
核心代码展示
/**
* @author hanyl
* @apiNote 鸭子抽象类
* @date 2024/2/27 16:11
*/
public abstract class Duck {
/**
* 具有飞翔行为的FlyBehavior接口
*/
FlyBehavior flyBehavior;
/**
* 具有呱呱叫行为的QuackBehavior接口
*/
QuackBehavior quackBehavior;
/**
* 外观抽象类
*
*/
public abstract void display();
/**
* 默认实现的游泳方法
*/
public void swim(){
System.out.println("All ducks float, even decoys!");
}
/**
* demo1.Duck 类不直接处理,委托给 quackBehavior 引用的对象
*/
public void performQuack(){
quackBehavior.quack();
}
/**
* demo1.Duck 类不直接处理,委托给 flyBehavior 引用的对象
*/
public void performFly(){
flyBehavior.fly();
}
}
/**
* @author hanyl
* @apiNote 绿头鸭
* @date 2024/2/27 16:21
*/
public class MallardDuck extends Duck{
public MallardDuck() {
// `demo1.MallardDuck` 调用的是 FlyWithWings 去作为 FlyBehavior 的具体实现
flyBehavior = new FlyWithWings();
// `demo1.MallardDuck` 调用的是 Quack 去作为 QuackBehavior 的具体实现
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a real Mallard duck");
}
}
进一步优化
在上面这个场景中,我们是通过构造方法实例化,那么如何实现动态的修改呢?
A:没错,可以为 Duck
鸭子抽象类中的 两个属性:FlyBehavior
和 QuackBehavior
进行属性的set 修改
public abstract class Duck {
// ignore
/**
* 赋予能够动态的修改属性的能力:Fly
* @param flyBehavior 新的Fly能力
*/
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
/**
* 赋予能够动态的修改属性的能力:Quack
* @param quackBehavior 新的Quack能力
*/
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
测试
Duck modelDuck = new ModelDuck();
modelDuck.performFly();
// 动态修改属性
modelDuck.setFlyBehavior(new FlyRocketPowered());
modelDuck.performFly();
UML 图
观察者模式 - Observe
定义
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。帮助对象知悉现况
帮助对象知悉现况,不会错过该对象感兴趣的事。对象在运行时可决定是否继续被通知。
案例分析
需求
根据气象站提供的实际气象数据WeatherData,追踪并展示气象信息。展示平台有:当前状况布告板、预测布告板、统计布告栏等。
UML图
实现后的 UML图
内置的Java观察者模式
实现后的 UML图
缺陷
Observable是一个类 :
- Java不支持多重继承。 这限制了Observable的复用潜力
- 只有继承了Observable, 才能够使用他的核心方法
核心代码
public class WeatherData extends Observable {
/**
* 温度
*/
private float temperature;
/**
* 湿度
*/
private float humidity;
/**
* 气压
*/
private float pressure;
/**
* 我们的构造器不再需要为了
* 记住观察者们而建立数据结
* 构了。
*/
public WeatherData() {
}
/**
* 测试数据的改变
*/
public void measurementsChanged() {
// 标识:指示状态已经改变
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
/**
* 现在构造器需要一Observable当参数,
* 并将CurrentConditionsDisplay对象登记成为观察者。
* @param observable 订阅主题
*/
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
/**
* Observer 的方法重写:接受推送过来的信息
*/
@Override
public void update(Observable observable, Object arg) {
if (observable instanceof WeatherData) {
WeatherData weatherData = (WeatherData)observable;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
/**
* DisplayElement 的方法重写
*/
@Override
public void display() {
System.out.println("【Java内置版】Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
总结
- 观察者模式:在对象之间定义一对多的依赖,当一个对象改变状态的时候,依赖他的对象就回收到通知并自动更新消息
- 消息的接受没有顺序性
- 使用此模式时,你可从被观察者处推(push)或拉(pull)数据(然而,推的方式被认为更“正确”)。
- 如果有必要的话,可以实现自己的Observable(类而不是接口)
- Swing大量使用观察者模式:点击按钮后,让观察者感应到Swing组件的不同类型事件
装饰者模式 - Decorator
定义
装饰者模式:动态地将责任附加到对象上。 若要扩展功能,装饰者提供了比继承更有弹性 的替代方案。
装饰者和被装饰对象有相同的超类型。
你可以用一个或多个装饰者包装一个对象
装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
- Component:抽象组件
- ConcreteComponent :具体组件
- Decorator:抽象装饰器
- ConcreteDecorator:具体的装饰器
案例分析
需求
咖啡店计算每种咖啡后的价格。饮料总共有多重基底:黑咖啡、拿铁等等,可以为每种基底加上辅料:奶泡、牛奶、豆浆等等。
UML图分析
- Component:抽象组件 -> 基底 Beverage
- ConcreteComponent :具体组件 -> 黑咖啡DarkRoast、拿铁
- Decorator:抽象装饰器 -> 辅料CondimentDecorator
- ConcreteDecorator:具体的装饰器 -> 奶泡、牛奶、豆浆Soy
转化后的UML图:
核心代码
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
/**
* 计算花费费用
*
* @return 总共费用
*/
public abstract double cost();
}
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "DarkRoast";
}
@Override
public double cost() {
return .99;
}
}
/**
* 【抽象装饰者】必须让Condiment Decorator能
* 够取代Beverage,所以将Condiment
* Decorator扩展自 Beverage 类。
*/
public abstract class CondimentDecorator extends Beverage {
Beverage beverage;
/**
* 所有的调料装饰者都必须重新实现getDescription()方法。
* @return
*/
@Override
public abstract String getDescription();
}
/**
* @author hanyl
* @apiNote 豆浆
*/
public class Soy extends CondimentDecorator {
/**
* 用个实例变量用来记录被装饰者
* @param beverage 被装饰者
*/
public Soy(Beverage beverage) {
this.beverage = beverage;
}
/**
* 完整的输出每个被装饰者的每个信息,
* 而不是仅仅描述当前的调料信息
*
* @return
*/
@Override
public String getDescription() {
// 1. 先利用委托,得到一个描述信息
// 2. 再进行附加
return beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
return .15 + beverage.cost();
}
}
总结
- **装饰者模式:**动态地将责任附加到对象上。
- 可以透明的插入装饰者(也就是说我只需要构造的时候指明一下被装饰者)
- 插入了大量的小类(具体装饰器,具体组件信息),不容易轻松理解, 程序容易复杂
工厂模式 - Abstract Method/Factory Method
定义
简易工厂模式:定义一个创建对象的接口,但由子类来决定要实例化类
**工厂方法模式:**定义一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
案例分析
简易工厂模式
需求
有很多披萨店,不同区域的披萨店有不同的披萨类型。
披萨的制作需要进行的工作如下:准备、烘焙、剪切、装盒。
制作披萨需要:披萨名称、面团类型、酱料类型、一套佐料。
披萨店呢,提供给用户下单服务并制作披萨。
不同地区的披萨店,可以制作不同种类的披萨。根据用户的下单请求,来制作用户需要的披萨。
public class DependentPizzaStore {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new NYStyleClamPizza();
} else if (type.equals("pepperoni")) {
pizza = new NYStylePepperoniPizza();
}
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if (type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
} else if (type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
} else if (type.equals("pepperoni")) {
pizza = new ChicagoStylePepperoniPizza();
}
} else {
System.out.println("Error: invalid type of pizza");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
UML图
披萨抽象类:共同的拥有名称、面团类型、酱料类型、一套佐料等,他们都需要进行准备工作
烘焙、剪切、装盒等工作,不同的披萨店使用的原料和制作方法细节各有不同。
从上面的需求中,我们可以知道。我们是根据用户的需求,调用选择好了披萨原型以后,再去制作
商店抽象类:下单方法,制作抽象方法。
核心代码
/**
* @author hanyl
* @apiNote 披萨抽象类
* @date 2024/3/3 18:40
*/
public abstract class Pizza {
/**
* 名称
*/
String name;
/**
* 面团
*/
String dough;
/**
* 酱料
*/
String sauce;
/**
* 佐料
*/
List<String> toppings = new ArrayList<String>();
/**
* 准备工作
*/
public void prepare() {
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
/**
* 烘焙
*/
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
/**
* 切片
*/
void cut() {
System.out.println("Cut the pizza into diagonal slices");
}
/**
* 打包
*/
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
@Override
public String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for (String topping : toppings) {
display.append(topping + "\n");
}
return display.toString();
}
}
/**
* @author hanyl
* @apiNote
* @date 2024/3/3 22:02
*/
public abstract class PizzaStore {
/**
* 工厂方法。通过指定不同的参数,来决定构建哪种披萨店铺
* @param item 指定创建的类型
* @return
*/
abstract Pizza createPizza(String item);
/**
* 下单接口
* @param type 指定创建的类型
* @return
*/
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
/**
* 共同方法
*/
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
/**
* 纽约风格的芝士披萨
* @author 16248
*/
* @author 16248
*/
public class NYStyleCheesePizza extends Pizza {
/**
* 构造纽约风格的芝士披萨的方法
*/
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
/**
* 纽约地区的披萨店.
* 不同地区的商店,能够制作出不同的披萨
* @author 16248
*/
public class NYPizzaStore extends PizzaStore {
/**
* 根据指明的类型。调用构造方法实例化对象
* @param item 指定创建的类型
* @return
*/
@Override
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}
public class PizzaTestDrive {
public static void main(String[] args) {
// 1. 用户选择在纽约披萨店下单
PizzaStore nyStore = new NYPizzaStore();
// 2. 用户在纽约披萨店下单的是 cheese 类型
Pizza pizza1 = nyStore.orderPizza("cheese");
// 3. 输出打印制作过程
System.out.println("Ethan ordered a " + pizza1.getName() + "\n");
}
}
抽象工厂模式
需求
在上面的基础上,不同的披萨构造的原料还不同。因此呢,我们还可以构建一个生产原料的工厂。
每种类型的披萨在不同地区的原料工厂需要的原料组成不同。
每种披萨都有面团、酱料、芝士、一些海鲜佐料。
UML图
核心代码
public abstract class Pizza {
protected String name;
protected Dough dough;
protected Sauce sauce;
protected Veggies veggies[];
protected Cheese cheese;
protected Pepperoni pepperoni;
protected Clams clam;
/**
* 由于每种披萨拥有不同的准备工作,因此改为抽象类
*/
public abstract void prepare();
void bake() {
System.out.println("2. Bake for 25 minutes at 350");
}
void cut() {
System.out.println("3. Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("4. Place pizza in official PizzaStore box");
}
public void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("---- " + name + " ----\n");
if (dough != null) {
result.append(dough);
result.append("\n");
}
if (sauce != null) {
result.append(sauce);
result.append("\n");
}
if (cheese != null) {
result.append(cheese);
result.append("\n");
}
if (veggies != null) {
for (int i = 0; i < veggies.length; i++) {
result.append(veggies[i]);
if (i < veggies.length-1) {
result.append(", ");
}
}
result.append("\n");
}
if (clam != null) {
result.append(clam);
result.append("\n");
}
if (pepperoni != null) {
result.append(pepperoni);
result.append("\n");
}
return result.toString();
}
}
/**
* @author hanyl
* @apiNote 原料工厂类
* @date 2024/3/4 13:56
*/
public interface PizzaIngredientFactory {
/**
* 制作面团
*
* @return Dough 面团
*/
public Dough createDough();
/**
* 制作调味酱
*
* @return Sauce 调味酱
*/
public Sauce createSauce();
/**
* 制作奶酪
* @return Cheese 奶酪
*/
public Cheese createCheese();
/**
* 蔬菜搭配
* @return Veggies[] 蔬菜数据合集
*/
public Veggies[] createVeggies();
/**
* 搭配香肠
*
* @return Pepperoni 香肠
*/
public Pepperoni createPepperoni();
/**
* 制作蛤蜊
*
* @return Clams 蛤蜊
*/
public Clams createClam();
}
这样,在具体的制作披萨过程,只需要指明一下我使用的是具体的哪一个原料工厂。
/**
* @author hanyl
* @apiNote 纽约地区的披萨原料制作工厂
* @date 2024/3/4 14:00
*/
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
/**
* 制作面团
*
* @return Dough 薄皮面团
*/
@Override
public Dough createDough() {
return new ThinCrustDough();
}
/**
* 制作调味酱
*
* @return Sauce 蕃茄酱
*/
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
/**
* 制作奶酪
* @return Cheese 雷吉亚诺奶酪
*/
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
/**
* 蔬菜搭配
* @return Veggies[] 蔬菜数据合集:大蒜、洋葱
*/
@Override
public Veggies[] createVeggies() {
Veggies veggies[] = { new Garlic(), new Onion() };
return veggies;
}
/**
* 搭配香肠
*
* @return Pepperoni 薄片香肠
*/
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
/**
* 制作蛤蜊
*
* @return Clams 新鲜蛤蜊
*/
@Override
public Clams createClam() {
return new FreshClams();
}
}
/**
* 奶酪披萨
* @author hanyl
*/
public class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
/**
* 通过不同的地区的原料工厂制作奶酪披萨
* @param ingredientFactory 不同的地区的原料工厂
*/
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
/**
* 奶酪披萨:制作面团、抹上酱料、铺上奶酪
*/
@Override
public void prepare() {
System.out.println("1. Preparing " + name);
dough = ingredientFactory.createDough();
System.out.println("\t" + dough);
sauce = ingredientFactory.createSauce();
System.out.println("\t" +sauce);
cheese = ingredientFactory.createCheese();
System.out.println("\t" +cheese);
}
}
接下来的话,我们开设纽约地区的披萨店:
- 先选择纽约区域的原料工厂
- 交由工厂类去制作这种类型的披萨
/**
* @author hanyl
* @apiNote 纽约披萨店
* @date 2024/3/4 14:16
*/
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String item) {
// 1. 需要返回的披萨
Pizza pizza = null;
// 2. 指定原料工厂为:纽约披萨原料工厂
PizzaIngredientFactory nyPizzaIngredientFactory = new NYPizzaIngredientFactory();
if (item.equals("cheese")){
pizza = new CheesePizza(nyPizzaIngredientFactory);
pizza.setName("New York Style Cheese Pizza");
}else if (item.equals("clam")){
pizza = new ClamPizza(nyPizzaIngredientFactory);
pizza.setName("New York Style clam Pizza");
}
return pizza;
}
}
总结
- 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
单例模式 - Singleton
单例模式:确保一个类只有一个实例,并提供全局访问点
在Java中实现单件模式需要私有的构造器、一个静态方法和一个静态变量。
双重检验锁
由于没有性能上的考虑,所以似乎杀鸡用了牛刀。适用于Jdk5 以上版本。
/**
* @author hanyl
* @apiNote
* @date 2024/3/5 11:49
*/
public class Singleton {
/**
* 利用静态变量来记录唯一的实例
*/
private volatile static Singleton singleton;
/**
* 构造器声明为私有的,这样就只有在内部类才能够实例化
*/
private Singleton() {
}
/**
* 获取唯一的实例化对象(双重检验锁)
*
* @return
*/
public static Singleton getInstance() {
// 如果静态唯一实例是空的,表示还没有创建实例
if (singleton == null) {
// 注意,只有第一次才彻底执行synchronized这里的代码。
synchronized (Singleton.class) {
if (singleton == null) {
// 而如果它不存在,我们就利用私有的构造器创建一个实例,并把它赋值给静态变量中
// “延迟实例化” : 也就是说:如果我们不调用这个方法,也就是永远不会实例化
singleton = new Singleton();
}
}
}
return singleton;
}
}
热实例化
适用于一定要产生一个实例对象的场景。
/**
* @author hanyl
* @apiNote
* @date 2024/3/5 11:49
*/
public class Singleton {
/**
* 利用静态变量来记录唯一的实例
*/
private static Singleton singleton = new Singleton();
/**
* 构造器声明为私有的,这样就只有在内部类才能够实例化
*/
private Singleton() {
}
/**
* 获取唯一的实例化对象(双重检验锁)
*
* @return
*/
public static Singleton getInstance() {
return singleton;
}
}
同步语句块
性能要求低,保证可行的最直接方法
/**
* @author hanyl
* @apiNote
* @date 2024/3/5 11:49
*/
public class Singleton {
/**
* 利用静态变量来记录唯一的实例
*/
private static Singleton singleton ;
/**
* 构造器声明为私有的,这样就只有在内部类才能够实例化
*/
private Singleton() {
}
/**
* 获取唯一的实例化对象
*
* @return
*/
public static synchronized Singleton getInstance() {
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
命令模式 - Command
定义
**命令模式:**将“请求”封装成对象,以便使用不同的请求队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
案例分析
需求
现有一个远程控制系统,远程控制系统可以随意的接入不同的家电。远程控制系统可以接受一组命令。而按动不同的按钮,可以执行相应的家电命令。
还可以进行撤销,回退到上一步的操作。
例如,现在为其接上:台灯(开、关)、音响(开、关)、风扇(调高、调低、关闭)
分析
首先,我们需要一组家电。
将“请求”封装成对象**:**
台灯开 、 台灯关、音响开、音响关、风扇调高、风扇调低、风扇关闭
为了进一步解耦,制作一个命令接口Command。而上述的命令都需要实现这个接口
继续制作一个远程控制系统,远程控制系统需要去接口一组命令。
其中,远程控制系统:
- 动态新增或者修改每一组的命令
- 调用命令
- 关闭命令
- 撤销回退到上一步的操作
如何实现撤销呢?可以这么做:
- 新增一个实例变量:存储最后被调用的命令undoCommand
- 之后,如果需要执行撤销方法,只需要再去获取这个命令,调用undo方法
为什么把命令变为“傻瓜式命令”?
这种操作解耦程度更高
如果需要设置一组宏命令呢?
不需要改变很多。依旧 implements Command,而实例变量则变为 一组命令对象 Command[] commands。
核心代码
/**
* 风扇家电类
* @author hanyl
*/
public class CeilingFan {
String location = "";
int level;
public static final int HIGH = 2;
public static final int MEDIUM = 1;
public static final int LOW = 0;
public CeilingFan(String location) {
this.location = location;
}
public void high() {
// turns the ceiling fan on to high
level = HIGH;
System.out.println(location + " ceiling fan is on high");
}
public void medium() {
// turns the ceiling fan on to medium
level = MEDIUM;
System.out.println(location + " ceiling fan is on medium");
}
public void low() {
// turns the ceiling fan on to low
level = LOW;
System.out.println(location + " ceiling fan is on low");
}
public void off() {
// turns the ceiling fan off
level = 0;
System.out.println(location + " ceiling fan is off");
}
public int getSpeed() {
return level;
}
}
public interface Command {
public void execute();
public void undo();
}
/**
* @author hanyl
* @apiNote 风扇调高命令
* @date 2024/3/7 16:46
*/
public class CeilingFanHighCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
prevSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
@Override
public void undo() {
switch (prevSpeed){
case CeilingFan.HIGH: ceilingFan.high(); break;
case CeilingFan.MEDIUM: ceilingFan.medium(); break;
case CeilingFan.LOW: ceilingFan.low(); break;
default: ceilingFan.off(); break;
}
}
}
/**
* @author hanyl
* @apiNote 宏命令组
* @date 2024/3/8 15:07
*/
public class MacroCommand implements Command{
Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (int i = 0; i < commands.length; i++) {
commands[i].execute();
}
}
@Override
public void undo() {
for (int i = 0; i < commands.length; i++) {
commands[i].undo();
}
}
}
/**
* @author hanyl
* @apiNote 远程控制器
* @date 2024/3/7 16:28
*/
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl() {
this.onCommands = new Command[7];
this.offCommands = new Command[7];
NoCommand noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
this.onCommands[i] = noCommand;
this.offCommands[i] = noCommand;
}
}
public void setCommandOns(int slot, Command commandOn, Command commandOff) {
this.onCommands[slot] = commandOn;
this.offCommands[slot] = commandOff;
}
public void onButtonWasPushed(int slot){
this.onCommands[slot].execute();
this.undoCommand = this.onCommands[slot];
}
public void offButtonWasPushed(int slot){
this.offCommands[slot].execute();
this.undoCommand = this.offCommands[slot];
}
public void undoButtonWasPushed() {
undoCommand.undo();
}
@Override
public String toString() {
StringBuffer stringBuff = new StringBuffer();
stringBuff.append("\n------ Remote Control -------\n");
for (int i = 0; i < onCommands.length; i++) {
stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()
+ " " + offCommands[i].getClass().getName() + "\n");
}
stringBuff.append("------ Remote Control -------\n");
return stringBuff.toString();
}
}
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
CeilingFan ceilingFan = new CeilingFan("起居室");
Command ceilingFanHighCommand = new CeilingFanHighCommand(ceilingFan);
Command ceilingFanMediumCommand = new CeilingFanMediumCommand(ceilingFan);
Command ceilingFanOffCommand = new CeilingFanOffCommand(ceilingFan);
remoteControl.setCommandOns(0, ceilingFanMediumCommand, ceilingFanOffCommand);
remoteControl.setCommandOns(1, ceilingFanHighCommand, ceilingFanOffCommand);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(1);
remoteControl.undoButtonWasPushed();
System.out.print("//\n" +
"// 宏命令组\n" +
"//\n");
Light light = new Light("起居室");
Command lightCommandOn = new LightOnCommand(light);
Command lightCommandOff = new LightOffCommand(light);
Stereo stereo = new Stereo("起居室");
Command stereoOffCommand = new StereoOffCommand(stereo);
Command stereoOnCommand = new StereoOnCommand(stereo);
Command[] firstCommands = {ceilingFanHighCommand, lightCommandOn, stereoOnCommand};
Command[] secondCommands = {ceilingFanOffCommand, lightCommandOff, stereoOffCommand};
Command macroCommand1 = new MacroCommand(firstCommands);
Command macroCommand2 = new MacroCommand(secondCommands);
RemoteControl control = new RemoteControl();
control.setCommandOns(2, macroCommand1, macroCommand2);
System.out.println("------------宏命令组:打开----------");
control.onButtonWasPushed(2);
System.out.println("------------宏命令组:关闭-----------");
control.offButtonWasPushed(2);
System.out.println("------------宏命令组:撤销------------");
control.undoButtonWasPushed();
}
}
总结
**命令模式:**将“请求”封装成对象,以便使用不同的请求队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
命令可以将运算块打包(一个接收者和一组动作)然后将它传来传去,就像是一般的对象一样。现在即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。
具体应用:
- 队列
某一端添加命令,然后另端则是线程。线程进行下面的动作:从队列中取出一个命令,调用它的execute()方法,等待这个调用完成:然后将此命令对象丢弃,再取出下一个命令……
- 日志 / 事务
通过新增两个方法(store()、load()),命令模式能够够支持这一点。
执行命令的时候,就调用 store()
宕机的时候,就调用 load()
适配器模式 - Adapter
定义
适配器模式:将一个类的接口,转换成客户期望的另个接口。适配器让原本接口不兼容的类可以合作无间。(转化接口)
你可以写一个类,将新厂商接口转接成你所期望的接口
需求
实现火鸡接口类能够转为为鸭子接口类
核心代码
// 实现需要转化的类型接口
public class TurkeyAdapter implements Duck {
Turkey turkey;
// 获取适配器的引用
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
@Override
public void quack() {
// 由于 Turkey 不会 quack ,于是就转化为 gobble
turkey.gobble();
}
@Override
public void fly() {
// 由于火鸡的飞翔距离很短,所以:连续调用来缩小两者飞翔距离的差距
for (int i = 0; i < 5; i++) {
turkey.fly();
}
}
}
测试类
public class DuckTestDrive {
public static void main(String[] args) {
// 绿头鸭
Duck duck = new MallardDuck();
// 火鸡
Turkey turkey = new WildTurkey();
// 伪装成鸭子的火鸡
TurkeyAdapter turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println("The Turkey says...");
turkey.gobble();
turkey.fly();
System.out.println("\nThe Duck says...");
testDuck(duck);
System.out.println("\nThe TurkeyAdapter says...");
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck) {
duck.quack();
duck.fly();
}
/**
* @Result:
*
* The Turkey says...
* Gobble gobble
* I'm flying a short distance
*
* The Duck says...
* Quack
* I'm flying
*
* The TurkeyAdapter says...
* Gobble gobble
* I'm flying a short distance
* I'm flying a short distance
* I'm flying a short distance
* I'm flying a short distance
* I'm flying a short distance
*
* 2024/3/13 20:04
*/
}
外观模式 - Facade
定义
外观模式(facade)–提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。(简化接口)
核心代码
/**
* 外观模式
*
* @author hanyl
*/
public class HomeTheaterFacade {
// 以下实例变量为:类
Amplifier amp;
Tuner tuner;
StreamingPlayer player;
CdPlayer cd;
Projector projector;
TheaterLights lights;
Screen screen;
PopcornPopper popper;
public HomeTheaterFacade(Amplifier amp,
Tuner tuner,
StreamingPlayer player,
Projector projector,
Screen screen,
TheaterLights lights,
PopcornPopper popper) {
this.amp = amp;
this.tuner = tuner;
this.player = player;
this.projector = projector;
this.screen = screen;
this.lights = lights;
this.popper = popper;
}
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setStreamingPlayer(player);
amp.setSurroundSound();
amp.setVolume(5);
player.on();
player.play(movie);
}
public void endMovie() {
System.out.println("Shutting movie theater down...");
popper.off();
lights.on();
screen.up();
projector.off();
amp.off();
player.stop();
player.off();
}
public void listenToRadio(double frequency) {
System.out.println("Tuning in the airwaves...");
tuner.on();
tuner.setFrequency(frequency);
amp.on();
amp.setVolume(5);
amp.setTuner(tuner);
}
public void endRadio() {
System.out.println("Shutting down the tuner...");
tuner.off();
amp.off();
}
}
模板方法模式 - Template Method
定义
**模板方法模式:**在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
子类定义其中某些步骤的内容。在算法中的个别步骤可以有不同的实现细节,但是算法的结构依然维持不变
工厂方法是模板方法的一种特殊版本。
核心代码
public abstract class CaffeineBeverageWithHook {
/**
* 模板方法
* 每个步骤都被一个方法代表了
* 某些方法由超类处理
* 某些方法由子类处理
*/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
// 通过钩子函数,来确定是不是需要调用
if (customerWantsCondiments()){
addCondiments();
}
}
// 必须需要由子类提供的方法,声明为抽象方法
abstract void brew();
abstract void addCondiments();
// 这个具体的方法被定义在抽象类中将它声明为final,这样一来子类就无法覆盖它。
// 它可以被模板方法直接使用或者被子类使用。
final void boilWater() {
System.out.println("Boiling water");
}
final void pourInCup() {
System.out.println("Pouring into cup");
}
// 我们也可以有“默认不做事的方法”:hook 钩子函数
// (可选性)子类可以视情况决定要不要覆盖它们
boolean customerWantsCondiments() {
return true;
}
}
迭代器模式 - Iterator
定义
迭代器模式(iterator): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
把游走的任务放在送代器上,而不是聚合上这样简化了聚合的接口和实现,也让责任各得其所。
核心代码
public class DinerMenuIterator implements Iterator<MenuItem> {
MenuItem[] menuItems;
int position = 0;
public DinerMenuIterator(MenuItem[] menuItems) {
this.menuItems = menuItems;
}
@Override
public boolean hasNext() {
return menuItems.length > position;
}
@Override
public MenuItem next() {
return menuItems[position++];
}
@Override
public void remove() {
if (position <= 0) {
throw new IllegalStateException
("You can't remove an item until you've done at least one next()");
}
// 因为使用的是固定长度的数组,所以在remove()被调用时,我们将后面的所有元素往前移动一个位置。
if (menuItems[position-1] != null) {
for (int i = position-1; i < (menuItems.length-1); i++) {
menuItems[i] = menuItems[i+1];
}
menuItems[menuItems.length-1] = null;
}
}
}
组合模式 - Composite
定义
组合模式(composite):组合模式–允许你将对象组成树形结构来表现“整体/部分”的层次结构。组合能让客户以一致的方式处理个别对象和对象组合。
有数个对象的集合,它们彼此之间有“整体/部分”的关系,并且你想用一致的方式对待这些对象时,你就需要我。
组合模式允许我们像对待单个对象一样对待对象集合。
包含其他组件的组件为组合对象
而没有包含其他组件的组件为叶节点对象
核心代码
import java.util.Iterator;
public abstract class MenuComponent {
// 组合节点的组合方法
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
// 叶子节点的操作方法
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
// 由子类具体实现
public abstract Iterator<MenuComponent> createIterator();
// 组合节点 和叶子节点均可重写
public void print() {
throw new UnsupportedOperationException();
}
}
import java.util.ArrayList;
import java.util.Iterator;
public class Menu extends MenuComponent {
Iterator<MenuComponent> iterator = null;
ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
@Override
public MenuComponent getChild(int i) {
return menuComponents.get(i);
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public Iterator<MenuComponent> createIterator() {
if (iterator == null) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
@Override
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = iterator.next();
menuComponent.print();
}
}
}
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public double getPrice() {
return price;
}
@Override
public boolean isVegetarian() {
return vegetarian;
}
@Override
public Iterator<MenuComponent> createIterator() {
return new NullIterator();
}
@Override
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
import java.util.Iterator;
import java.util.Stack;
public class CompositeIterator implements Iterator<MenuComponent> {
Stack<Iterator<MenuComponent>> stack = new Stack<Iterator<MenuComponent>>();
public CompositeIterator(Iterator<MenuComponent> iterator) {
stack.push(iterator);
}
@Override
public MenuComponent next() {
if (hasNext()) {
Iterator<MenuComponent> iterator = stack.peek();
MenuComponent component = iterator.next();
stack.push(component.createIterator());
return component;
} else {
return null;
}
}
@Override
public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator<MenuComponent> iterator = stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}
/*
* No longer needed as of Java 8
*
* (non-Javadoc)
* @see java.util.Iterator#remove()
*
public void remove() {
throw new UnsupportedOperationException();
}
*/
}
/*
* 解决某些
*/
public class NullIterator implements Iterator<MenuComponent> {
public MenuComponent next() {
return null;
}
public boolean hasNext() {
return false;
}
/*
* No longer needed as of Java 8
*
* (non-Javadoc)
* @see java.util.Iterator#remove()
*
public void remove() {
throw new UnsupportedOperationException();
}
*/
}
这样,我们在使用调用的时候,就可以统一:
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
public void printVegetarianMenu() {
Iterator<MenuComponent> iterator = allMenus.createIterator();
System.out.println("\nVEGETARIAN MENU\n----");
while (iterator.hasNext()) {
MenuComponent menuComponent = iterator.next();
try {
if (menuComponent.isVegetarian()) {
menuComponent.print();
}
} catch (UnsupportedOperationException e) {}
}
}
}
状态模式 - State
定义
状态模式–允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态图:
状态图转化为代码:
类图
核心代码
接口类或者抽象类,高度抽象状态
public interface State {
/**
* 付款
*/
public void insertQuarter();
/**
* 退款
*/
public void ejectQuarter();
/**
* 转动曲柄
*/
public void turnCrank();
/**
* 发放糖果
*/
public void dispense();
/**
* 重新填入糖果
*/
public void refill();
}
用一个Context标识状态之间的流转
public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
}
/**
* 付款
*/
public void insertQuarter() {
state.insertQuarter();
}
/**
* 退款
*/
public void ejectQuarter() {
state.ejectQuarter();
}
/**
* 转动曲柄
*/
public void turnCrank() {
state.turnCrank();
state.dispense();
}
// 辅助方法
/**
* 释放糖果
*/
void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (count > 0) {
count = count - 1;
}
}
/**
* 重复投币
*/
void refill(int count) {
this.count += count;
System.out.println("The gumball machine was just refilled; its new count is: " + this.count);
state.refill();
}
void setState(State state) {
this.state = state;
}
int getCount() {
return count;
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public State getWinnerState() {
return winnerState;
}
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
以某个状态举例
public class HasQuarterState implements State {
Random randomWinner = new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
/**
* 付款
*/
@Override
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
/**
* 退款
*/
@Override
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
/**
* 转动曲柄
*/
@Override
public void turnCrank() {
System.out.println("You turned...");
int winner = randomWinner.nextInt(10);
if ((winner == 0) && (gumballMachine.getCount() > 1)) {
gumballMachine.setState(gumballMachine.getWinnerState());
} else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
/**
* 发放糖果
*/
@Override
public void dispense() {
System.out.println("No gumball dispensed");
}
/**
* 重新填入糖果
*/
@Override
public void refill() { }
@Override
public String toString() {
return "waiting for turn of crank";
}
}
代理模式 - Proxy
房产中介、经纪人等等
定义
代理模式(proxy):控制对象的访问,对另一个对象提供一个替身或占位符以访问这个对象。
远程对象交互通讯,访问开销大的对象,控制对象的访问… …
需求
举例:约会服务对象系统,要求自己能够修改自己的基础信息;评分只能由他人评分
——使用Java的动态代理实现保护代理
要修正这些问题,你必须创建两个代理:
- 一个访问你自己的PersonBean对象
- 另一个访问另一顾客的PersonBean对象
这样,代理就可以控制在每一种情况下允许哪一种请求
核心代码
public class NonOwnerInvocationHandler implements InvocationHandler {
Person person;
public NonOwnerInvocationHandler(Person person) {
this.person = person;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
// nonOwnerProxy.setGeekRating(3);
// method.getName() : setGeekRating
// method.invoke((person, args):其中,person被代理的对象是 Person, args参数为 10
// 也就是说,实际上是调用了 Person 的 setGeekRating,里面的参数为 10
try {
// 它的getName()方法,我们就可以知道proxy被调用的方法是什么。
if (method.getName().startsWith("get")) {
// 原始proxy被调用的方法。这个对象在调用时被传给我们。只不过加载调用的是真正的主题(person)
return method.invoke(person, args);
} else if (method.getName().equals("setGeekRating")) {
return method.invoke(person, args);
} else if (method.getName().startsWith("set")) {
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
System.out.println("--------------------------------------------------------------");
// 获取代理对象
// Proxy本身是利用静态的Proxy.newProxyInstance()方法在运行时动态地创建的。
Person nonOwnerProxy = (Person) Proxy.newProxyInstance(
joe.getClass().getClassLoader(),
joe.getClass().getInterfaces(),
new NonOwnerInvocationHandler(joe));
System.out.println("名称:" + nonOwnerProxy.getName()+ "\n" + joe);
System.out.println("【非法操作开始】");
try {
System.out.println("Q:非拥有者代理对象设置属性:Interests?");
nonOwnerProxy.setInterests("保龄球, 高尔夫");
} catch (Exception e) {
System.out.println("A:非拥有者代理对象设置属性:Interests —— 失败");
}
System.out.println("【非法操作结束】");
System.out.println("--------------------------------------------------------------");
System.out.println("名称:" + nonOwnerProxy.getName()+ "\n" + joe);
System.out.println("【合法操作开始】");
System.out.println("Q:非拥有者代理尝试对象设置属性:Rating?");
nonOwnerProxy.setGeekRating(3);
System.out.println("A:非拥有者代理尝试对象设置属性:Rating —— 成功");
System.out.println("【合法操作结束】");
System.out.println("当前的Rating是:" + nonOwnerProxy.getGeekRating());
System.out.println("--------------------------------------------------------------");
总结
思维导图
模式的分类
根据模式的目标分类
创建型
涉及到将对象实例化。这类模式都提供一个方法,将客户从所需要实例化的对象中解耦出来。
- 单例模式
- 抽象工厂模式
- 工厂方法模式
- Prototype
- Builfer
行为型
只要是行为型模式,都涉及到类和对象如何交互及分配职责。
- 模板方法模式
- 命令模式
- 迭代器模式
- 观察者模式
- 状态模式
- 策略模式
- Chain of Responsibility
- Visitor
- Mediator
- Memento
- Interpreter
结构型
结构型模式可以让你把类或对象组合到更大的结构中。
- 装饰器模式
- 代理模式
- 组合模式
- 外观模式
- 适配器模式
- Flyweight
- Bridge
模式所处理的对象
类
- 类模式描述类之间的关系如何通过继承定义。
- 类模式的关系是在编译时建立的。
- 模板方法模式
- 工厂方法模式
- 适配器模式
- Interpreter
对象
- 对象模式描述对象之间的关系,而且主要是利用组合定义。
- 对象模式的关系通常在运行时建立,而且更加动态、更有弹性
- 组合模式
- 装饰器模式
- 代理模式
- 策略模式
- 抽象工厂模式
- 单例模式
- 状态模式
- 观察者模式
- 外观模式
- 命令模式
- 迭代器模式
- Builder
- Bridge
- Flyweight
- Prototype
- Chain of Responsibility
- Mediator
- Memento
- Visitor