创建型模式
用于描述怎么创建对象,主要特点是将对象创建与使用分离。
有单例、工厂方法、抽象工厂、原型和建造者这五种模式。
结构型模式
用于描述如何将类或对象按某种布局组成更大的结构。
有代理、适配器、桥接、装饰、外观、享元和组合这七种模式。
行为型模式
用于描述类或对象之间怎样相互协同,共同完成单个对象无法完成的任务,以及怎样分配职责。
有模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式
这种模式关注点是:如何创建对象。它将对象创建与使用分离开来,使用者不用关注创建的细节,降低了系统耦合。
单一的某个类负责创建自己的对象,并保证只有一个对象被创建出来,在类内部已经实例化好对象,给外部提供一个方法供其访问,外部就不必实例化。
1.1 单例模式结构
1.2 单例模式分类
1.2.1 饿汉式-静态变量方式
步骤如下:
(1) 构造器私有化(防止外部new一个实例化对象)
(2) 类的内部创建对象
(3) 给外部提供一个方法获取实例化对象
public class Singleton {
private Singleton() {
}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
优缺点说明:
1.2.2 饿汉式-静态代码块方式
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
static {
singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
跟静态变量方式类似,只不过将实例化过程放在了静态代码块中。优缺点也与静态变量方式一致。
1.2.3 懒汉式(线程不安全)
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
优缺点说明:
1.2.4 懒汉式(线程安全)
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static synchronized Singleton getInstance() {
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
优缺点说明:
1.2.5 懒汉式(双重检查锁)
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优缺点说明:
解决方法:对静态变量添加volatile关键字
public class Singleton {
private Singleton() {
}
private static volatile Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1.2.6 懒汉式(静态内部类)
由内部类实例化对象。在加载外部类时,不会加载静态内部类,只有当静态内部类中属性或方法被调用时,才会被加载。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder{
static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
}
优点:避免了线程不安全,利用类加载机制实现了懒加载,效率较高。
1.2.7 恶汉式(枚举类)
enum Singleton {
INSTANCE;
}
优点:枚举类先天就是线程安全的,还能防止反序列化创建新的对象。
注意事项:
举例:
设计一个咖啡店点餐系统。咖啡店【CoffeeStore】具有点咖啡功能,可以点美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】,咖啡制作过程为加牛奶,加糖。
在以前的做法中,直接new对象,会造成类之间耦合严重,当增加新的咖啡类时,orderCoffee得修改,违反了开闭原则。所以,我们要使用工厂模式,用工厂来生产对象,我们只要和工厂打交道就可以了。当对象需要修改时,只要在工厂内修改就好了。因此,工厂模式最大的优点:解耦。
2.1 简单工厂模式
简单工厂模式结构
由SimpleCoffeeFactory来创建对象,coffeeStore中的orderCoffee方法调用工厂方法中的createCoffee来获取对象。解除了coffeeStore和
具体实现类之间的耦合。但是同时增加了coffeeStore和工厂的耦合,工厂和具体实现类的耦合。
优缺点说明:
2.2 工厂方法模式
定义一个用于创建对象的接口,让子类去决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式结构
具体工厂
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
咖啡店类
public class CoffeeStore(){
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory){
this.factory = factory;
}
public Coffee orderCoffee(){
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
优点:
缺点:
2.3 抽象工厂模式
工厂方法模式考虑的是一类产品的生产,如畜牧场只养动物,电视机厂只生产电视。
这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只生产同等级的产品,但是在现实生活中,大多是综合性的工厂,能生产多等级的产品,电器厂既生产电视机,又生产洗衣机,空调等。
抽象工厂模式考虑的是多等级产品的生产,将同一个具体工厂所生产的位于不同等级的产品称为一个产品族。
抽象工厂模式结构:
现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义抽象甜品类,提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
结构:
原型模式的克隆分为浅克隆和深克隆:
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个“三好学生”奖状出来,然后在修改奖状上的名字即可。
public class Citation implements Cloneable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void show(){
System.out.println(name + "获得了三好学生奖状");
}
}
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
Citation c1 = new Citation();
c1.setName("张三");
Citation c2 = (Citation) c1.clone();
c2.setName("李四");
c1.show();
c2.show();
}
}
注意事项与细节:
建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。
建造者模式是一步步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
4.1 建造者模式的四个角色:
4.3 实例
创建共享单车。生产自行车是一个复杂的过程,它包含了车架、车座等组件的生产。而车架又有碳纤维,铝合金等,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。
这里Bike是产品,包含车架,车座等组件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具体的建造者;Director是指挥者。类图如下:
//自行车类
public class Bike {
private String frame;
private String seat;
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
//抽象Builder类
public abstract class Builder {
protected Bike bike = new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
public abstract Bike createBike();
}
//摩拜单车Builder类
public class MobikeBuilder extends Builder {
@Override
public void buildFrame() {
bike.setFrame("铝合金框架");
}
@Override
public void buildSeat() {
bike.setSeat("橡胶座椅");
}
@Override
public Bike createBike() {
return bike;
}
}
//ofoBuilder类
public class OfoBuilder extends Builder {
@Override
public void buildFrame() {
bike.setFrame("碳纤维框架");
}
@Override
public void buildSeat() {
bike.setSeat("真皮座椅");
}
@Override
public Bike createBike() {
return bike;
}
}
//指挥者类
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Bike construct(){
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
//用户类
public class Client {
public static void main(String[] args) {
Director director = new Director(new OfoBuilder());
Bike bike = director.construct();
System.out.println(bike.getFrame());
System.out.println(bike.getSeat());
}
}
4.4 优缺点说明
优点:
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间差异性很大,则不适合使用建造者模式,因此其适用范围受到一定限制。
4.5 对比
工厂方法模式VS建造者模式:
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
举个简单例子来说明两者差异,如果要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷,能够飞翔,内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式VS建造者模式:
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理在编译期就生成,而动态代理类则是在Java运行时动态生成。动态代理又有JDK代理和Cglib代理两种。
1.1 结构
1.2 静态代理
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。类图如下:
//抽象主题类
public interface SellTickets {
void sell();
}
//真实主题类
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
//代理类
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
@Override
public void sell() {
System.out.println("收取手续费");
station.sell();
}
}
//用户
public class Client {
public static void main(String[] args) {
ProxyPoint proxy = new ProxyPoint();
proxy.sell();
}
}
从中可以看出,用户直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell()方法进行了增强(代理点收取一些费用)。
1.3 JDK动态代理
Java中提供了一个动态代理类Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。
//抽象主题类
public interface SellTickets {
void sell();
}
//真实主题类
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
//JDK动态代理
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject(){
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(), station.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收取手续费");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//用户类
public class Client {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
SellTickets proxyObject = proxyFactory.getProxyObject();
proxyObject.sell();
}
}
执行流程如下:
1.4 动态代理和静态代理对比:
动态代理与静态代理相比,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。这样,在接口数量比较多的时候,我们可以灵活处理,而不需像静态代理那样在每一个方法中中转。
如果接口增加一个方法,静态代理除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护复杂度。
1.5 代理模式优点:
优点:
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
2.1 结构
2.2 类适配器模式
实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
类图如下:
//tf卡接口
public interface TFCard {
String readTF();
void writeTF(String message);
}
//tf卡实现类
public class TFCardImpl implements TFCard {
@Override
public String readTF() {
String msg = "read TF card";
return msg;
}
@Override
public void writeTF(String message) {
System.out.println("write TFCard:" + message);
}
}
//sd卡接口
public interface SDCard {
String readSD();
void writeSD(String message);
}
//适配器类
public class SDCardAdapterTFCard extends TFCardImpl implements SDCard {
@Override
public String readSD() {
System.out.println("adapter read tf card ");
return readTF();
}
@Override
public void writeSD(String message) {
System.out.println("adapter write tf card");
writeTF(message);
}
}
//电脑类
public class Computer {
public String readSD(SDCard sdCard){
return sdCard.readSD();
}
}
//用户类
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
SDCard adapter = new SDCardAdapterTFCard();
computer.readSD(adapter);
}
}
由上述代码可知,类适配器模式违背了合成复用原则。同时由于适配器继承了TFCardImpl类,一个类只能继承于一个父类,所以,这个适配器只能为这一个类服务。
2.3 对象适配器模式
实现方式:对象适配器模式可采用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
//适配器类
public class SDCardAdapterTFCard implements SDCard {
private TFCard tfCard;
public SDCardAdapterTFCard(TFCard tfCard) {
this.tfCard = tfCard;
}
@Override
public String readSD() {
System.out.println("adapter read tf card ");
return tfCard.readTF();
}
@Override
public void writeSD(String message) {
System.out.println("adapter write tf card");
tfCard.writeTF(message);
}
}
//用户类
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
TFCard tfCard = new TFCardImpl();
SDCard adapter = new SDCardAdapterTFCard(tfCard);
computer.readSD(adapter);
}
}
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
3.1 结构
快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。
//快餐接口
public abstract class FastFood {
private float price;
private String desc;
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public abstract float cost();
}
//炒饭
public class FriedRice extends FastFood {
public FriedRice() {
super(12,"一份炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
//炒面
public class FriedNoodles extends FastFood {
public FriedNoodles() {
super(10,"一份炒面");
}
@Override
public float cost() {
return getPrice();
}
}
//配料类
public abstract class Garnish extends FastFood {
private FastFood fastFood;
public Garnish(FastFood fastFood,float price, String desc) {
super(price, desc);
this.fastFood = fastFood;
}
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
}
//鸡蛋配料
public class Egg extends Garnish {
public Egg(FastFood fastFood) {
super(fastFood, 1,"一个鸡蛋");
}
@Override
public float cost() {
return getFastFood().cost() + getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
//培根配料
public class Bacon extends Garnish {
public Bacon(FastFood fastFood) {
super(fastFood, 2,"一个培根");
}
@Override
public float cost() {
return getFastFood().cost() + getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
//用户类
public class Client {
public static void main(String[] args) {
FastFood fastFood = new FriedNoodles();
System.out.println(fastFood.getDesc() +"价格:" + fastFood.cost());
System.out.println("=======================");
FastFood fastFood1 = new Egg(fastFood);
System.out.println(fastFood1.getDesc()+ "价格" + fastFood1.cost());
System.out.println("========================");
FastFood fastFood2 = new Egg(fastFood1);
System.out.println(fastFood2.getDesc()+ "价格" + fastFood2.cost());
System.out.println("========================");
}
}
3.2 优点
3.3 代理和装饰者区别
相同点:
1、都要实现与目标类相同的业务接口
2、在两个类中都要声明目标对象
3、都可以在不修改目标类的前提下增强目标方法
不同点:
1、目的不同。装饰者是为了增强目标对象,静态代理是为了保护和隐藏目标对象。
2、获取目标对象构建的地方不同。装饰者是由外界传递进来,可以通过构造方法传递。静态代理是在代理内部创建,以此来隐藏目标对象。
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
4.1 结构
需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。
//视频文件
public interface VideoFile {
void decode(String fileName);
}
//avi文件
public class AVIFile implements VideoFile {
@Override
public void decode(String fileName) {
System.out.println("AVI视频文件" + fileName);
}
}
//rmvb文件
public class RMVBFile implements VideoFile {
@Override
public void decode(String fileName) {
System.out.println("RMVB视频文件" + fileName);
}
}
//操作系统
public abstract class OperatingSystem {
protected VideoFile videoFile;
public OperatingSystem(VideoFile videoFile) {
this.videoFile = videoFile;
}
public abstract void play(String fileName);
}
//windows
public class Windows extends OperatingSystem {
public Windows(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
//mac
public class Mac extends OperatingSystem {
public Mac(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String fileName) {
videoFile.decode(fileName);
}
}
4.2 优点
又名门面模式,是一种通过多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高程序的可维护性。
5.1 结构
小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭。
//灯类
public class Light {
public void on(){
System.out.println("打开灯");
}
public void off(){
System.out.println("关闭灯");
}
}
//电视类
public class TV {
public void on(){
System.out.println("打开电视");
}
public void off(){
System.out.println("关闭电视");
}
}
//空调类
public class AirCondition {
public void on(){
System.out.println("打开空调");
}
public void off(){
System.out.println("关闭空调");
}
}
//智能音箱
public class SmartApplicationFacade {
private Light light;
private TV tv;
private AirCondition airCondition;
public SmartApplicationFacade() {
this.light = new Light();
this.tv = new TV();
this.airCondition = new AirCondition();
}
public void say(String message){
if(message.contains("on")){
on();
}else if(message.contains("off")){
off();
}else{
System.out.println("我听不懂您说的");
}
}
private void on(){
light.on();
tv.on();
airCondition.on();
}
private void off(){
light.off();
tv.off();
airCondition.off();
}
}
//用户类
public class Client {
public static void main(String[] args) {
SmartApplicationFacade smartApplicationFacade = new SmartApplicationFacade();
smartApplicationFacade.say("on");
}
}
5.2 优缺点说明
优点:
缺点:
不符合开闭原则,修改很麻烦。
又名部分整体模式。是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
6.1 结构
如下图,我们在访问别的一些管理系统时,经常可以看到类似的菜单。一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单,因此使用组合模式描述菜单就很恰当,我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。
//菜单组件
public abstract class MenuComponent {
protected String name;
protected int level;
public MenuComponent(String name, int level) {
this.name = name;
this.level = level;
}
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(){
return name;
}
public void print(){
throw new UnsupportedOperationException();
}
}
//菜单
public class Menu extends MenuComponent {
private List<MenuComponent> list;
public Menu(String name,int level) {
super(name,level);
list = new ArrayList<>();
}
@Override
public void add(MenuComponent menuComponent) {
list.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
list.remove(menuComponent);
}
@Override
public MenuComponent getChild(int i) {
return list.get(i);
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("==");
}
System.out.println(name);
for (MenuComponent menuComponent : list) {
menuComponent.print();
}
}
}
//菜单项
public class MenuItem extends MenuComponent{
public MenuItem(String name, int level) {
super(name, level);
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("==");
}
System.out.println(name);
}
}
//用户类
public class Client {
public static void main(String[] args) {
MenuComponent menu1 = new Menu("菜单管理", 2);
menu1.add(new MenuItem("页面访问",3));
menu1.add(new MenuItem("展开菜单",3));
menu1.add(new MenuItem("编辑菜单",3));
menu1.add(new MenuItem("删除菜单",3));
menu1.add(new MenuItem("新增菜单",3));
MenuComponent menu2 = new Menu("权限配置", 2);
menu2.add(new MenuItem("页面访问",3));
menu2.add(new MenuItem("提交保存",3));
MenuComponent menu3 = new Menu("角色管理", 2);
menu3.add(new MenuItem("页面访问",3));
menu3.add(new MenuItem("新增角色",3));
menu3.add(new MenuItem("修改角色",3));
MenuComponent menu = new Menu("系统管理",1);
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu.print();
}
6.2 优点
运用共享技术来有效的支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。
享元模式存在以下两种状态:
7.1 结构
下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。
//抽象方块类
public abstract class AbstractBox {
public abstract String getShape();
public void display(String color){
System.out.println("形状是:" + getShape() + ",颜色是:" + color);
}
}
//I型方块
public class IBox extends AbstractBox{
@Override
public String getShape() {
return "I";
}
}
//L型方块
public class LBox extends AbstractBox{
@Override
public String getShape() {
return "L";
}
}
//O型方块
public class OBox extends AbstractBox{
@Override
public String getShape() {
return "O";
}
}
//工厂类
public class BoxFactory {
private HashMap<String,AbstractBox> map;
public BoxFactory() {
map = new HashMap<>();
AbstractBox IBox = new IBox();
AbstractBox LBox = new LBox();
AbstractBox OBox = new OBox();
map.put("I",IBox);
map.put("L",LBox);
map.put("O",OBox);
}
public static BoxFactory getInstance(){
return new BoxFactory();
}
public AbstractBox getBox(String key){
return map.get(key);
}
}
//用户类
public class Client {
public static void main(String[] args) {
BoxFactory boxFactory = BoxFactory.getInstance();
AbstractBox box = boxFactory.getBox("I");
box.display("红色");
}
}
7.2 优缺点说明
优点:
缺点:
为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂。
在面向对象程序设计过程中,程序员常常遇到这种情况:设计一个系统时,知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
1.1 结构
抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
模板方法 :定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
具体子类:实现抽象类中定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。
类图如下:
//抽象类
public abstract class AbstractClass {
public void cookProcess(){
pourOil();
heatOil();
pourVegetables();
pourSauce();
fry();
}
public void pourOil(){
System.out.println("倒油");
}
public void heatOil(){
System.out.println("热油");
}
public abstract void pourVegetables();
public abstract void pourSauce();
public void fry(){
System.out.println("翻炒");
}
}
//菜心具体子类
public class CaiXin extends AbstractClass {
@Override
public void pourVegetables() {
System.out.println("下菜心");
}
@Override
public void pourSauce() {
System.out.println("下蒜蓉");
}
}
//包菜具体子类
public class BaoCai extends AbstractClass {
@Override
public void pourVegetables() {
System.out.println("下包菜");
}
@Override
public void pourSauce() {
System.out.println("下辣椒");
}
}
//用户类
public class Client {
public static void main(String[] args) {
AbstractClass abstractClass = new CaiXin();
abstractClass.cookProcess();
}
}
1.2 优缺点说明:
优点:
缺点:
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
2.1 结构
一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。
//抽象策略类
public interface Strategy {
void show();
}
//具体策略类A
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("买一送一");
}
}
//具体策略类B
public class StrategyB implements Strategy {
@Override
public void show() {
System.out.println("满一百减五十");
}
}
//具体策略类C
public class StrategyC implements Strategy {
@Override
public void show() {
System.out.println("送三十元优惠券");
}
}
//环境角色
public class SalesMan {
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
public void salesManShow(){
strategy.show();
}
}
//用户类
public class Client {
public static void main(String[] args) {
Strategy strategy = new StrategyB();
SalesMan salesMan = new SalesMan(strategy);
salesMan.salesManShow();
}
}
2.1 优缺点说明
优点:
缺点:
将一个请求封装成一个对象,使发出请求的责任与执行请求的责任分隔开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用和管理。
3.1 结构
服务员:请求者,由她来发起命令。
资深大厨:接收者,真正执行命令的对象。
订单:命令中包含订单。
//抽象命令类
public interface Command {
void execute();
}
//订单
public class Order {
private int diningTable;
private Map<String,Integer> foodDic = new HashMap<>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFoodDic(String name,int num) {
foodDic.put(name,num);
}
}
//厨师
public class SeniorChef {
public void makeFood(int num,String foodName){
System.out.println(num + "份"+ foodName);
}
}
//具体命令类
public class OrderCommand implements Command{
private SeniorChef seniorChef;
private Order order;
public OrderCommand(SeniorChef seniorChef, Order order) {
this.seniorChef = seniorChef;
this.order = order;
}
@Override
public void execute() {
System.out.println(order.getDiningTable()+"桌的订单");
Set<String> keys = order.getFoodDic().keySet();
for (String key : keys) {
seniorChef.makeFood(order.getFoodDic().get(key), key);
}
System.out.println(order.getDiningTable()+"桌订单完成");
}
}
//调用者类
public class Waitor {
private ArrayList<Command> commands = new ArrayList<>();
public void setCommand(Command cmd) {
commands.add(cmd);
}
public void orderUp(){
System.out.println("叮咚,新订单来了");
for (Command command : commands) {
command.execute();
}
}
}
//用户类
public class Client {
public static void main(String[] args) {
SeniorChef seniorChef = new SeniorChef();
Order order1 = new Order();
order1.setDiningTable(1);
order1.setFoodDic("爆炒肉丝",1);
order1.setFoodDic("溜肥肠",2);
Order order2 = new Order();
order2.setDiningTable(2);
order2.setFoodDic("白菜炖豆腐",1);
order2.setFoodDic("烤鸭",3);
OrderCommand command1 = new OrderCommand(seniorChef,order1);
OrderCommand command2 = new OrderCommand(seniorChef,order2);
Waitor waitor = new Waitor();
waitor.setCommand(command1);
waitor.setCommand(command2);
waitor.orderUp();
}
}
2.2 优缺点说明
优点:
缺点:
又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链,当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
4.1 结构
现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
类图如下:
//抽象处理者角色
public abstract class Handler {
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
private int numStart;
private int numEnd;
private Handler nextHandler;
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public void submit(LeaveRequest leave){
if(leave.getNum() >= this.numStart){
this.handleLeave(leave);
}
if(this.nextHandler != null && leave.getNum() > this.numEnd){
this.nextHandler.handleLeave(leave);
}else{
System.out.println("流程结束");
}
}
public abstract void handleLeave(LeaveRequest leave);
}
//小组长
public class GroupLEader extends Handler{
public GroupLEader() {
super(0,Handler.NUM_ONE);
}
@Override
public void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "因" + leave.getContent()+ "请假"+ leave.getNum() + "天");
System.out.println("小组长审批通过");
}
}
//部门经理
public class Manager extends Handler{
public Manager() {
super(Handler.NUM_ONE,Handler.NUM_THREE);
}
@Override
public void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "因" + leave.getContent()+ "请假"+ leave.getNum() + "天");
System.out.println("部门经理审批通过");
}
}
//总经理
public class GeneralManager extends Handler{
public GeneralManager() {
super(Handler.NUM_THREE,Handler.NUM_SEVEN);
}
@Override
public void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "因" + leave.getContent()+ "请假"+ leave.getNum() + "天");
System.out.println("总经理审批通过");
}
}
//请假条
public class LeaveRequest {
private String name;
private int num;
private String content;
public LeaveRequest(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
public String getContent() {
return content;
}
}
//用户类
public class Client {
public static void main(String[] args) {
LeaveRequest request = new LeaveRequest("小王",5,"胃疼去医院");
GroupLEader groupLEader = new GroupLEader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
groupLEader.setNextHandler(manager);
manager.setNextHandler(generalManager);
groupLEader.submit(request);
}
}
4.2 优缺点说明
优点:
缺点:
对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
5.1 结构
通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。
类图如下:
//抽象状态类
public abstract class LiftState {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
public abstract void open();
public abstract void close();
public abstract void stop();
public abstract void run();
}
//开启状态类
public class OpeningState extends LiftState {
@Override
public void open() {
System.out.println("电梯门开启");
}
@Override
public void close() {
super.context.setLiftState(Context.CLOSING_STATE);
super.context.close();
}
@Override
public void stop() {
}
@Override
public void run() {
}
}
//关闭状态类
public class ClosingState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.open();
}
@Override
public void close() {
System.out.println("电梯门关闭");
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
@Override
public void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.run();
}
}
//停止状态类
public class StoppingState extends LiftState {
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.open();
}
@Override
public void close() {
super.context.setLiftState(Context.CLOSING_STATE);
super.context.close();
}
@Override
public void stop() {
System.out.println("电梯停止了");
}
@Override
public void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.run();
}
}
//运行状态类
public class RunningState extends LiftState {
@Override
public void open() {
}
@Override
public void close() {
}
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
@Override
public void run() {
System.out.println("电梯开始运行");
}
}
//环境类
public class Context {
public static final OpeningState OPENING_STATE = new OpeningState();
public static final ClosingState CLOSING_STATE = new ClosingState();
public static final StoppingState STOPPING_STATE = new StoppingState();
public static final RunningState RUNNING_STATE = new RunningState();
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
this.liftState.setContext(this);
}
public void open(){
liftState.open();
}
public void close(){
liftState.close();
}
public void stop(){
liftState.stop();
}
public void run(){
liftState.run();
}
}
//用户类
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.run();
context.close();
context.stop();
}
}
5.2 优缺点说明
优点:
缺点:
又被称为发布-订阅模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使它们能够自动更新自己。
6.1 结构
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。
//抽象主题类
public interface Subject {
public void attach(Observer observer);
public void detach(Observer observer);
public void notify(String message);
}
//具体主题类
public class SubscriptionSubject implements Subject {
private List<Observer> weixinUserList = new ArrayList<>();
@Override
public void attach(Observer observer) {
weixinUserList.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserList.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : weixinUserList) {
observer.update(message);
}
}
}
//抽象观察者类
public interface Observer {
public void update(String message);
}
//具体观察者类
public class WeixinUser implements Observer {
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "---" + message);
}
}
//用户类
public class Client {
public static void main(String[] args) {
Subject subject = new SubscriptionSubject();
WeixinUser user1 = new WeixinUser("张三");
WeixinUser user2 = new WeixinUser("李四");
WeixinUser user3 = new WeixinUser("王五");
subject.attach(user1);
subject.attach(user2);
subject.attach(user3);
subject.notify("公众号更新了");
}
}
6.2 优缺点说明
优点:
缺点:
又叫调停模式,定义一个中介角色来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变他们之间的交互。
7.1 结构
现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。
//抽象中介者
public interface Mediator {
public void contact(String message,Person person);
}
//抽象同事类
public abstract class Person {
protected String name;
protected Mediator mediator;
public Person(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
}
}
//租客类
public class Tenant extends Person {
public Tenant(String name, Mediator mediator) {
super(name, mediator);
}
public void contact(String message){
mediator.contact(message,this);
}
public void getMessage(String message){
System.out.println("租客" + name + "获取到的信息:" + message);
}
}
//房主类
public class HouseOwner extends Person {
public HouseOwner(String name, Mediator mediator) {
super(name, mediator);
}
public void contact(String message){
mediator.contact(message,this);
}
public void getMessage(String message){
System.out.println("房主" + name + "获取到的信息:" + message);
}
}
//具体中介类
public class MediatorStructure implements Mediator {
public HouseOwner houseOwner;
public Tenant tenant;
public HouseOwner getHouseOwner() {
return houseOwner;
}
public void setHouseOwner(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
@Override
public void contact(String message, Person person) {
if(person == tenant){
houseOwner.getMessage(message);
}
if(person == houseOwner){
tenant.getMessage(message);
}
}
}
//用户类
public class Client {
public static void main(String[] args) {
MediatorStructure mediator = new MediatorStructure();
HouseOwner houseOwner = new HouseOwner("张三",mediator);
Tenant tenant = new Tenant("李四",mediator);
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
tenant.contact("我想租一个两居室");
}
}
7.2 优缺点说明
优点:
缺点:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
8.1 结构
定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现。
类图如下:
//student类
public class Student {
private String name;
private String number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
'}';
}
}
//迭代器接口
public interface StudentIterator {
public boolean hasNext();
public Student next();
}
//具体的迭代器类
public class StudentIteratorImpl implements StudentIterator {
public List<Student> list;
private int position = 0;
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Student next() {
Student student = list.get(position);
++position;
return student;
}
}
//抽象容器类
public interface StudentAggregate {
public void addStudent(Student student);
public void removeStudent(Student student);
public StudentIterator getStudentIterator();
}
//具体容器类
public class StudentAggregateImpl implements StudentAggregate {
private List<Student> list = new ArrayList<>();
@Override
public void addStudent(Student student) {
list.add(student);
}
@Override
public void removeStudent(Student student) {
list.remove(student);
}
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
//用户类
public class Client {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("张三");
s1.setNumber("1");
Student s2 = new Student();
s2.setName("李四");
s2.setNumber("2");
StudentAggregate list = new StudentAggregateImpl();
list.addStudent(s1);
list.addStudent(s2);
StudentIterator iterator = list.getStudentIterator();
while(iterator.hasNext()){
Student student = iterator.next();
System.out.println(student);
}
}
}
8.2 优缺点说明
优点:
缺点:增加了类的个数,这在一定程度上增加了系统复杂度。
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新操作。
9.1 结构
现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。
//抽象访问者接口
public interface Person {
void feed(Cat cat);
void feed(Dog dog);
}
//具体访问者角色(主人)
public class Owner implements Person {
@Override
public void feed(Cat cat) {
System.out.println("主人喂猫");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂狗");
}
}
//具体访问者角色(其他人)
public class Someone implements Person {
@Override
public void feed(Cat cat) {
System.out.println("其他人喂猫");
}
@Override
public void feed(Dog dog) {
System.out.println("其他人喂狗");
}
}
//抽象元素角色
public interface Animal {
void accept(Person person);
}
//具体元素角色(狗)
public class Dog implements Animal{
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真好吃,汪汪汪");
}
}
//具体元素角色(猫)
public class Cat implements Animal{
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真好吃,喵喵喵");
}
}
//对象结构
public class Home {
private List<Animal> nodeList = new ArrayList<>();
public void action(Person person){
for (Animal animal : nodeList) {
animal.accept(person);
}
}
public void add(Animal animal){
nodeList.add(animal);
}
}
//用户类
public class Client {
public static void main(String[] args) {
Home home = new Home();
Owner owner = new Owner();
Someone someone = new Someone();
Dog dog = new Dog();
Cat cat = new Cat();
home.add(dog);
home.add(cat);
home.action(owner);
}
}
9.2 优缺点说明
优点:
缺点:
又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
10.1 结构
游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
//游戏角色类
public class GameRole {
private int vit;
private int atk;
private int def;
public void initState(){
this.vit = 100;
this.atk = 100;
this.def = 100;
}
public void fight(){
this.vit = 0;
this.atk = 0;
this.def = 0;
}
public RoleStateMemento saveState(){
return new RoleStateMemento(vit,atk,def);
}
public void recoverState(RoleStateMemento roleStateMemento){
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
public void stateDisplay(){
System.out.println("角色生命力:" + vit);
System.out.println("角色攻击力:" + atk);
System.out.println("角色防御力:" + def);
}
}
//备忘录类
public class RoleStateMemento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public int getAtk() {
return atk;
}
public int getDef() {
return def;
}
}
//管理者类
public class RoleStateCaretaker {
private RoleStateMemento roleStateMemento;
public RoleStateMemento getRoleStateMemento() {
return roleStateMemento;
}
public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
this.roleStateMemento = roleStateMemento;
}
}
//用户类
public class Client {
public static void main(String[] args) {
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
gameRole.fight();
gameRole.stateDisplay();
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.stateDisplay();
}
}
10.2 优缺点说明
优点:
缺点: