设计模式
理念
作用
使代码具有更好的:
- 代码重用性:相同代码不用多次编写
- 可读性 :编程规范
- 可扩展性 :添加新功能方便
- 可靠性:添加新功能,对原有功能没有影响
- 高内聚,低耦合
七大原则
-
单一职责原则
一个类只负责一个职责
-
接口隔离原则
客户端不应该依赖它不需要的接口
一个类对另一个类保存最小接口(依赖)
-
依赖倒置原则
高层模块不应该依赖于底层模块,二者都应该依赖其抽象 抽象不应该依赖细节,细节应该依赖抽象
依赖倒转中心思路是面向接口编程
-
里氏替换原则
子类集成父类,尽量不修改父类方法
-
开闭原则
对于扩展是开放的,对于修改是关闭的
-
迪米特法则
最少知道原则:一个对象应该对其他对象保持最少了解(类间关系越密切,耦合度越大)。
定义:只与直接的朋友通信。即提供接口,隐藏逻辑
核心:降低类之间的耦合关系(减少不必要的依赖)
-
合成复用原则
尽量使用合成、聚合的方式而不是使用继承
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2**.针对接口编程**,而不是针对实现编程。
3.为了交互对象之间的松耦合设计而努力
继承又称为白箱复用,相当于把所有的实现细节暴露给子类,组合/聚合又被称为黑箱复用,对类以外的对象是无法获取实现细节的。
设计原则的核心思想
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程,而不是针对实现编程
- 为了交互对象之间的松耦合设计而努力
单例模式
概念
采取一定的方法保证在整个的软件系统中,
对同一类型类只能存在一个对象对象实例,
并且该类只提供一个取得其对象实例的方法
使用场景
- **对于需要频繁创建的销毁的对象,使用单例模式提供系统性能**
- 对于创建对象时耗时过多或耗费资源过多的对象(重量级对象),但又**经常用到**的对象,**工具类**对象,**频繁访问**数据库或文件的对象(数据源,session工厂等)
饿汉式(静态常量)
方式
- 构造器私有化
- 类的内部创建对象(静态常量)
- 向外暴露静态公共方法:getInstance
- 代码实现
class A{
private static final A a = new A(new Date().getTime() + "");
private final String string;
private A(String string) {
this.string = string;
}
public static A getInstance(){
return a;
}
public String getString() {
return string;
}
}
优缺点
优点:
- 实现简单
- 避免多线程同步问题
缺点:
- 在类装载时即完成实例化,未达到Lazy Loading效果
- 若未使用过,会造成内存浪费
###饿汉式(静态代码块)
方式
- 构造器私有化
- 类的内部创建对象(静态代码快)
- 向外暴露静态公共方法:getInstance
- 代码实现
//饿汉式(静态代码块)
class B {
private static final B b;
private final String string = "s";
private B() {
}
static {
b = new B();
}
public static B getInstance() {
return b;
}
}
优缺点
优点:
- 实现简单
- 避免多线程同步问题
缺点:
- 在类装载时即完成实例化,未达到Lazy Loading效果
- 若未使用过,会造成内存浪费
懒汉式(线程不安全)
方式
-
构造器私有化
-
向外暴露静态公共方法:getInstance
-
在公共方法中通过判断静态变量是否为空决定是否创建
-
代码实现
//懒汉式(线程不安全) class C { private static C c; private String string; private C() {} public static C getInstance() { if (c == null) { c = new C(); } return c; } }
优缺点
优点:
- 起到懒加载效果(lazy laoding)
缺点:
- 只能在单线程使用
- 实际开发中,不能使用这种方法
懒汉式(线程安全,同步方法)
方式
在懒汉式(线程不安全)
基础上个体暴露方法添加synchronized
标签
public static synchronized C getInstance() {
if (c == null) {
c = new C();
}
return c;
}
优缺点
优点:
- 起到懒加载效果(lazy loading)
- 解决线程不安全问题
缺点:
- 效率太低
- 实际开发中,不推荐使用这种方法
###懒汉式(线程安全,同步代码块)
class D {
private static D c;
private String string;
private D() {
}
public static D getInstance() {
if (c == null) {
synchronized (D.class) {
c = new D();
}
}
return c;
}
}
无法解决线程安全,和效率问题
双重检查
推荐使用
方式
-
构造器私有化
-
向外暴露静态公共方法:getInstance
-
在公共方法中进行双重检查,第一重检查是否为空,第二重同步代码块中再次判断是否为空
-
代码实现
//双重检查 class E { private static E c; private String string; private E() { } public static E getInstance() { if (c == null) { synchronized (E.class) { if (c == null) { c = new E(); } } } return c; } }
静态内部类
推荐使用
方式
- 构造器私有化
- 实现静态内部类 其中含有静态属性Singleton
- 向外暴露静态公共方法:getInstance,返回私有静态内中静态属性
- 代码实现
//静态内部类
class F {
public String string;
private F() {
}
private static class FInstance {
private static final F F = new F();
static {
F.string = "123";
}
}
public static F getInstance() {
return FInstance.F;
}
}
优缺点
优点:
- 起到懒加载效果(lazy loading)
- 使用JVM类装载时线程安全机制保证线程安全
###枚举
推荐使用
枚举可以实现单例模式,不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象
enum G {
INSTANCE;
public void say() {
System.out.println("ok");
}
}
工厂模式
概念
专门负责将大量有共同接口的类实例化,工厂模式可以动态决定将哪一个类实例化,不必事先知道要实例化那一个类。
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到与主项目的依赖关系的解耦合,提高项目的维护性和扩展性
设计模式的依赖倒置原则
作用场景
重复,或批量创建同一类,同一类型的对象
简单工厂模式(静态工厂模式)
介绍
简单工厂模式属于创建型模式,是工厂模式的一种。
是由一个工厂对象决定创建出那以后总产品类的实例。
定义一个创建对象的类,由这个类来封装实例化对象的行为
实现
- 定义一个基类
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 实体类
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println(name + "披萨!!");
}
}
public class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println(name + "披萨");
}
}
3.实现一个工厂
//简单工厂类
public class SimpleFactory {
private static Pizza pizza;
public static Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new CheesePizza();
pizza.setName("奶酪");
break;
case "greek":
pizza = new GreekPizza();
pizza.setName("奶酪");
break;
default:
break;
}
return pizza;
}
}
- 调用工厂
public class SimpleFactoryTest {
public static void main(String[] args) {
SimpleFactory.createPizza("greek").prepare();
SimpleFactory.createPizza("greek").bake();
SimpleFactory.createPizza("cheese").prepare();
}
}
输出结果
奶酪披萨
奶酪baking
奶酪披萨!!
工厂方法模式
介绍
定义一个创建对象的抽象方法,由子类决定要实例化的类。
工厂方法模式将对象的实例化推迟到子类
实现
- 实体类添加一个判断字段
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println(name + "!!");
}
}
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
setName("北京希腊披萨");
System.out.println(name + "!!");
}
}
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦奶酪披萨");
System.out.println(name + "!!");
}
}
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦希腊披萨");
System.out.println(name + "!!");
}
}
- 拆分工厂
//工厂基类
abstract class OrderPizza {
protected Pizza pizza;
abstract Pizza createPizza(String type);
public static Pizza getPizza(String country, String type) {
switch (country) {
case "bj":
return new BJOrderPizza().createPizza(type);
case "ld":
return new LDOrderPizza().createPizza(type);
default:
return null;
}
}
}
//工厂实现类
public class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "greek":
pizza = new BJGreekPizza();
break;
default:
break;
}
return pizza;
}
}
//工厂实现类
public class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new LDCheesePizza();
break;
case "greek":
pizza = new LDGreekPizza();
break;
default:
break;
}
return pizza;
}
}
-
具体使用
public static void main(String[] args) { Pizza pizza = OrderPizza.getPizza("ld", "greek"); assert pizza != null; pizza.prepare(); }
//输出结果 伦敦希腊披萨!!
抽象工厂模式
介绍
顶一个接口用于创建相关或由依赖关系的对象簇,而无需指明具体的类
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。是对简单工厂模式的进一步抽象,将工厂抽象成两层,AbsFactory和具体实现的工厂子类
实现
- 确定一个抽象接口
//抽象层
public interface AbsFactory {
Pizza createPizza(String orderType);
}
- 子工厂继承并实现接口
public class BJFactory implements AbsFactory {
private Pizza pizza;
@Override
public Pizza createPizza(String type) {
switch (type) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "greek":
pizza = new BJGreekPizza();
break;
default:
break;
}
return pizza;
}
}
- 封装操作
public class OrderPizza {
private static AbsFactory absFactory;
public static void setAbsFactory(AbsFactory factory) {
absFactory = factory;
}
public static Pizza getPizza(String type) {
if (absFactory != null) {
return absFactory.createPizza(type);
} else
return null;
}
}
- 操作
public static void main(String[] args) {
OrderPizza.setAbsFactory(new LDFactory());
Pizza cheese = OrderPizza.getPizza("cheese");
assert cheese != null;
cheese.prepare();
}
输出结果
伦敦奶酪披萨!!
原型模式
概念
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,允许一个对象在创建另一个可定制的对象。可以由工厂方法由克隆方法来实现。
作用场景
实现逻辑
- 实体类继承
Cloneable
接口 - 重写
clone()
方法
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
public Sheep() {
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
public Sheep getClone() throws CloneNotSupportedException {
return (Sheep) clone();
}
}
使用
Sheep sheep = new Sheep("tom", 12, "red");
Sheep clone = sheep.getClone();
System.out.println(sheep);
System.out.println(clone);
输出结果
Sheep{name='tom', age=12, color='red'}
Sheep{name='tom', age=12, color='red'}
浅拷贝
介绍
对于数据类型为基本类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值赋值一份给新的对象
对于数据类型为引用类型的成员变量,浅拷贝会进行引用传递,将该成员变量的内存地址复制一份给新的对象。
举例
public static void main(String[] args) throws CloneNotSupportedException {
List<String> list = new ArrayList<>();
Sheep sheep = new Sheep("tom", 12, "red");
sheep.setList(list);
Sheep clone = sheep.getClone();
System.out.println(sheep);
System.out.println(clone);
list.add("Red");
System.out.println(sheep);
System.out.println(clone);
}
输出结果
Sheep{name='tom', age=12, color='red', list=[]}
Sheep{name='tom', age=12, color='red', list=[]}
Sheep{name='tom', age=12, color='red', list=[Red]}
Sheep{name='tom', age=12, color='red', list=[Red]}
深拷贝
介绍
赋值对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每隔引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。
方式
-
重写clone方法实现
基本类型直接调用父类进行
复杂类型单独进行转换
-
通过对象序列化实现
通过序列化与反序列化进行对象克隆
//通过对象序列化实现 public Object deepClone() { //创建流对象 ByteArrayOutputStream bos; ObjectOutputStream oos; ByteArrayInputStream bis; ObjectInputStream ois; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this);//当前对象以对象流方式输出 //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); Object object = ois.readObject(); bos.close(); oos.close(); bis.close(); ois.close(); return object; } catch (Exception e) { e.printStackTrace(); return null; } }
建造者模式
概念
Builder pattern,又叫生成器模式,是一种对象构建模式,它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造不同的属性的对象
允许用户只通过指定复杂对象的类型和内容构建他们,用户不需要知道内部的具体构建细节
作用场景
创建复杂对象
抽象工厂模式只关心由什么工厂生产
建造者模式主要目的通过组装生产新的产品
操作流程
- 确定产品原型
public class House {
private String basic;
private String walls;
private String roofs;
public String getBasic() {
return basic;
}
public void setBasic(String basic) {
this.basic = basic;
}
public String getWalls() {
return walls;
}
public void setWalls(String walls) {
this.walls = walls;
}
public String getRoofs() {
return roofs;
}
public void setRoofs(String roofs) {
this.roofs = roofs;
}
@Override
public String toString() {
return "House{" +
"basic='" + basic + '\'' +
", walls='" + walls + '\'' +
", roofs='" + roofs + '\'' +
'}';
}
}
- 创建一个建造者基类
public abstract class HouseBuilder {
protected House house = new House();
public abstract HouseBuilder buildBasic(String value);
public abstract HouseBuilder buildWalls(String value);
public abstract HouseBuilder buildRoofs(String value);
public House build() {
return house;
}
}
- 实现具体建造者
public class CommonHouse extends HouseBuilder{
@Override
public HouseBuilder buildBasic(String value) {
house.setBasic(value);
return this;
}
@Override
public HouseBuilder buildWalls(String value) {
house.setWalls(value);
return this;
}
@Override
public HouseBuilder buildRoofs(String value) {
house.setRoofs(value);
return this;
}
}
- 使用链式操作直接构建
public static void main(String[] args) {
House build = new CommonHouse().buildBasic("Asda").build();
System.out.println(build);
House build1 = new CommonHouse().buildBasic("地基").buildWalls("四墙").buildRoofs("屋顶").build();
System.out.println(build1);
}
输入结果
House{basic='Asda', walls='null', roofs='null'}
House{basic='地基', walls='四墙', roofs='屋顶'}
适配器模式
概念
Adapter pattern 将某个类的接口转换成客户端期望的另一个接口表示, 主要目的是兼容性 让原本因接口不匹配不能一起工作的两个类可以协同工作
适配器模式属于结构型模式
别名:包装器(Wrapper)
作用场景
类适配器模式
介绍
Adapter类 通过继承src类,实现dst类接口,完成src --> dst 的适配
实例
被适配类
public class Voltage220V {
public int output220V() {
int src = 220;
System.out.println("电压" + src + "伏");
return src;
}
}
适配接口
public interface Voltage5V {
int output5V();
}
适配器
public class VoltageAdapter extends Voltage220V implements Voltage5V{
@Override
public int output5V() {
int i = output220V();
return i/44;
}
}
使用者
public class Phone {
public void charging(Voltage5V voltage5V){
if (voltage5V.output5V() == 5){
System.out.println("true");
}else {
System.out.println("false");
}
}
}
调用
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
输出接口
电压220伏
true
注意事项
- 类适配器需要继承src类 , 且dst类必须是接口,有一定局限性
- src类的方法在Adapter中都会暴露,增加使用成本
- 由于继承src,可以根据需求重写src类的方法,使得Adapter的灵活性增加
对象适配器模式
介绍
基本思路与类适配器相同
修改Adapter类 不继承src类,而是持有src类的实例,以解决兼容性问题。
即: 持有src类,实现dst类接口,完成src--> dst的适配
根据合成复用原则:系统中尽量使用关联关系来代替继承关系
实例
修改适配器
public class VoltageAdapter implements Voltage5V {
private final Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
int dst = 0;
if (voltage220V != null) {
int src = voltage220V.output220V();
dst = src / 44;
}
return dst;
}
}
调用
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
输出结果
电压220伏
true
接口适配器模式
介绍
Default Adatper Pattern (缺省适配器模式)
当不需要全部实现接口提供的方法时,可以设计一个抽象类实现接口,并且为该接口的每隔方法提供一个默认实现(空方法)。抽象类的子类就可有选择的覆盖父类的某些方法。
实例
若一个接口存在四个方法
public interface Voltage5V {
int m1();
void m2();
void m3();
void m4();
}
创建抽象类预实现
public abstract class AbsAdapter implements Voltage5V {
public int m1() {
return 0;
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
}
使用
public static void main(String[] args) {
Voltage5V absAdapter = new AbsAdapter() {
@Override
public int m1() {
return 2;
}
};
System.out.println(absAdapter.m1());
}
输出结果
2
注意事项
适用于一个接口不想使用其所有的方法的情况
桥接模式
概念
Bridge pattern :将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
结构型设计模式,基于类的最小设计原则,通过使用封装、聚合以及继承等行为让不同的类承担不同的职责
特点:把抽象与行为实现分离开,从而保持各部分的独立性以及应对他们的功能扩展
作用场景
多功能扩展时
实现逻辑
- 定义实现类接口
public interface Brand {
void open();
void close();
void call();
}
- 具体实现类实现接口
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("XiaoMi open...");
}
@Override
public void close() {
System.out.println("XiaoMi close...");
}
@Override
public void call() {
System.out.println("XiaoMi call...");
}
}
public class HuaWei implements Brand{
@Override
public void open() {
System.out.println("HuaWei open...");
}
@Override
public void close() {
System.out.println("HuaWei close...");
}
@Override
public void call() {
System.out.println("HuaWei call...");
}
}
- 定义抽象层抽象类接口
public abstract class Phone {
private final 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("FoldedPhone");
}
public void close() {
super.close();
System.out.println("FoldedPhone");
}
public void call() {
super.call();
System.out.println("FoldedPhone");
}
}
public class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}
public void close() {
super.close();
System.out.println("UpRightPhone");
}
public void call() {
super.call();
System.out.println("UpRightPhone");
}
}
- 调用
public class BridgeTest {
public static void main(String[] args) {
//使用FoldedPhone类型的XiaoMi
Phone phone = new FoldedPhone(new XiaoMi());
phone.open();
//使用UpRightPhone类型的XiaoMi
Phone phone2 = new UpRightPhone(new XiaoMi());
//可以直接使用XiaoMi的方法,也可进行包装截断
phone2.open();
}
}
输出结果
XiaoMi open...
FoldedPhone
XiaoMi open...
注意事项
- 实现抽象和实现部分的分离,有助于系统的分层设计,产生更好的结构化系统
- 对于系统高层,只需要知道抽象部分与实现部分的街廓,其他部分由具体业务完成
- 桥接模式代替多层继承,减少子类个数,降低系统的管理和维护
- 由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计
- 要求正确识别系统两个独立变化的维度,其使用范围具有一定局限性,。
装饰者模式
概念
动态的将新功能附加到对象上,在对象功能扩展方面,比继承更有弹性,装饰者模式体现了开闭原则(OCP)
通过递归方式 方便组合和维护
作用场景
多对象进行组合使用,减少类创建
实例
-
定义被装饰者抽象类
public abstract class Drink { private String description; private float price = 0f; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public abstract float cost(); @Override public String toString() { return "Drink{" + "description='" + description + '\'' + ", price=" + price + '}'; } }
-
装饰者具体类缓冲层
public class Coffee extends Drink{ @Override public float cost() { return super.getPrice(); } }
-
装饰者具体类
public class BlackCoffee extends Coffee{ public BlackCoffee() { setDescription("BlackCoffee"); setPrice(30f); } }
public class ShortCoffee extends Coffee { public ShortCoffee() { setDescription("ShortCoffee"); setPrice(20f); } }
-
装饰器继承被装饰者
public class Decorator extends Drink { private final Drink drink; public Decorator(Drink drink) { this.drink = drink; } @Override public float cost() { return super.getPrice() + drink.cost(); } @Override public String getDescription() { return super.getDescription() + ":" + super.getPrice() + " && " + drink.getDescription(); } @Override public String toString() { return super.toString() + " " + drink; } }
-
装饰者继承装饰器
public class Milk extends Decorator{ public Milk(Drink drink) { super(drink); setDescription("Milk"); setPrice(2f); } }
public class Chocolate extends Decorator{ public Chocolate(Drink drink) { super(drink); setDescription("Chocolate"); setPrice(10f); } }
public class Soy extends Decorator{ public Soy(Drink drink) { super(drink); setDescription("Soy"); setPrice(5f); } }
-
调用
public class DecoratorTest { public static void main(String[] args) { Drink blackCoffee = new ShortCoffee(); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Chocolate(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Milk(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); blackCoffee = new Soy(blackCoffee); System.out.println(blackCoffee.getDescription()); System.out.println(blackCoffee.cost()); System.out.println("------------"); System.out.println(blackCoffee); } }
输出结果
ShortCoffee
20.0
------------
Chocolate:10.0 && ShortCoffee
30.0
------------
Milk:2.0 && Chocolate:10.0 && ShortCoffee
32.0
------------
Soy:5.0 && Milk:2.0 && Chocolate:10.0 && ShortCoffee
37.0
------------
Drink{description='Soy', price=5.0} Drink{description='Milk', price=2.0} Drink{description='Chocolate', price=10.0} Drink{description='ShortCoffee', price=20.0}
Process finished with exit code 0
组合模式
概念
Composite pattern :部分整体模式,创建对象组的树形结构,将对象组合成树状结构以表示整体-部分的层次关系
属于结构型模式,使得用户对单个对象和组合对象的访问局有一致性。即组合能让昂客户以一致的方式处理个别对象以及组合对象
作用场景
当要处理的对象可以生辰一个树形结构,而我们要对树的节点或叶子进行操作时,能够提供一致的方式而不需要考虑它是节点还是叶子
实例
-
创建一个基类
public abstract class OrganizationComponent { private String name; private String des; public OrganizationComponent(String name, String des) { this.name = name; this.des = des; } protected void add(OrganizationComponent component) { throw new UnsupportedOperationException(); } protected void remove(OrganizationComponent component) { throw new UnsupportedOperationException(); } protected abstract void print(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } }
-
创建一级类
public class University extends OrganizationComponent { //存放College private final List<OrganizationComponent> list = new ArrayList<>(); public University(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent component) { list.add(component); } @Override protected void remove(OrganizationComponent component) { list.remove(component); } @Override protected void print() { System.out.println("----------" + getName() + "------------"); for (OrganizationComponent organizationComponent : list) { organizationComponent.print(); } } }
-
创建二级类
public class College extends OrganizationComponent { //存放department private final List<OrganizationComponent> list = new ArrayList<>(); public College(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent component) { list.add(component); } @Override protected void remove(OrganizationComponent component) { list.remove(component); } @Override protected void print() { System.out.println("----------" + getName() + "------------"); for (OrganizationComponent organizationComponent : list) { organizationComponent.print(); } } }
-
创建叶子类
public class Department extends OrganizationComponent{ public Department(String name, String des) { super(name, des); } @Override public String getName() { return super.getName(); } @Override protected void print() { System.out.println(getName()); } }
-
调用
public class CompositeTest { public static void main(String[] args) { OrganizationComponent university = new University("清华", "大学"); OrganizationComponent college1 = new College("计算机学院", "计算机"); OrganizationComponent college2 = new College("软件学院", "软件"); college1.add(new Department("计算机科学与技术","computer")); Department department = new Department("大数据", "computer"); college1.add(department); college2.add(new Department("软件工程","软件工程")); university.add(college1); university.add(college2); university.print(); college1.remove(department); university.print(); university.remove(college1); university.print(); } }
输出结果
----------清华------------
----------计算机学院------------
计算机科学与技术
大数据
----------软件学院------------
软件工程
----------清华------------
----------计算机学院------------
计算机科学与技术
----------软件学院------------
软件工程
----------清华------------
----------软件学院------------
软件工程
注意事项
- 简化客户端操作,客户端只需要面对一致的对象而不需要面对整体部分或节点叶子问题
- 具有较强扩展性,更改组合对象时,只需要调整内部的层次关系
- 方便创建复杂的层次结构,容易创建节点或叶子从而构建树形结构
- 需要遍历组织结构,或处理对象具有树形结构
- 要求较高的抽象性,要求节点和叶子的差异性小,若差异性过大则无法使用
外观模式
概念
Facade pattern 也叫过程模式: 为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,使这一子系统更加容易使用
屏蔽内部子系统的细节。
作用场景
封装多对象的操作,系统需要进行分层设计时
实例
-
定义方法对象
public class DVDPlayer { //使用单例模式 private static final DVDPlayer instance = new DVDPlayer(); private DVDPlayer() {} public static DVDPlayer getInstance(){ return instance; } public void open(){ System.out.println("DVDPlayer open..."); } public void off(){ System.out.println("DVDPlayer off..."); } public void play(){ System.out.println("DVDPlayer play..."); } }
public class Popcorn { private static final Popcorn instance = new Popcorn(); private Popcorn() { } public static Popcorn getInstance() { return instance; } public void on(){ System.out.println("Popcorn on..."); } public void off(){ System.out.println("Popcorn off..."); } public void pop(){ System.out.println("Popcorn pop..."); } }
-
封装操作
public class Facade { private final DVDPlayer dvdPlayer; private final Popcorn popcorn; public Facade() { dvdPlayer = DVDPlayer.getInstance(); popcorn = Popcorn.getInstance(); } public void open() { dvdPlayer.open(); popcorn.on(); } public void play() { dvdPlayer.play(); } public void pop() { popcorn.pop(); } public void close() { dvdPlayer.off(); popcorn.off(); } }
-
调用
public class FacadeTest { public static void main(String[] args) { Facade facade = new Facade(); facade.open(); facade.pop(); facade.play(); facade.close(); } }
输出结果
DVDPlayer open...
Popcorn on...
Popcorn pop...
DVDPlayer play...
DVDPlayer off...
Popcorn off...
注意事项
- 屏蔽子系统的细节,降低客户端对子系统使用的复杂性
- 当系统需要进行分层设计时,考虑使用Facade模式
- 维护大型系统时,开发Facade类提供遗留系统的较清晰简单的接口,提高复用性
- 不能过多或者使用不合理的使用外观模式,以系统有层次,便于维护为目的选择使用
享元模式
概念
Flyweight pattern 也叫蝇量模式 : 运用共享技术有效的支持大量细粒度 的对象
享元模式能够解决重复对象的内存浪费问题,当系统存在大量相似对象时,设置缓冲池并从中获取对象以避免创建新对象。达到降低系统内存,提高效率目的
享元模式提出要求:细粒度和共享对象
对象信息分为内部状态和外部状态
-
内部状态
对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
-
外部状态
对象得以依赖的一个标记,随环境改变而改变,不可共享的状态
作用场景
常用于系统底层开发,解决系统的性能问题,如数据库连接池
经典应用场景:池技术
实例
-
定义抽象类
public abstract class WebSite { abstract void use(User user); }
-
具体实现类继承抽象类
public class ConcreteWebSite extends WebSite { //type即为享元模式的内部状态 共享内容 private String type; public ConcreteWebSite(String type) { this.type = type; } @Override void use(User user) { System.out.println(user.getName() + " use type: " + type); } }
-
定义外部状态类
public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
创建工厂类 提供享元获取方式
public class WevSiteFactory { private final HashMap<String, ConcreteWebSite> pool = new HashMap<>(); public WebSite getWebSiteCategory(String type) { if (!pool.containsKey(type)) { pool.put(type, new ConcreteWebSite(type)); } return pool.get(type); } public int getWebSiteCount() { return pool.size(); } }
-
调用
public class FlyWeightTest { public static void main(String[] args) { WevSiteFactory wevSiteFactory = new WevSiteFactory(); WebSite webSite = wevSiteFactory.getWebSiteCategory("new"); WebSite webSite2 = wevSiteFactory.getWebSiteCategory("model"); User user = new User("test"); User user2 = new User("user"); webSite.use(user); webSite.use(user2); webSite2.use(user); webSite2.use(user2); System.out.println(wevSiteFactory.getWebSiteCount()); } }
输出结果
test use type: new
user use type: new
test use type: model
user use type: model
2
注意事项
- 系统中有大量对象,并且消耗大量内存,并且对象的状态大部分可以外部化时,可以考虑用享元模式
- 用唯一标识码判断,如果内存中有,则返回这个唯一表示码 的对象用HashMap/HashTable存储
- 享元模式极大减少对象的创建,降低程序内存的占用,
- 提高了系统的复杂度,需要分离内部状态和外部状态,外部状态具有固化特性**,不应随内部状态的改变而改变,这是使用享元模式需要注意的点**
- 使用时应注意划分内部状态和外部状态,并且需要一个工厂类加以控制
- 应用场景是需要缓冲池的场景如String常量池、数据库连接池
代理模式
概念
Proxy pattern :为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。
好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能
被代理对象可以是远程对象、创建开销大的对象或需要安全控制的对象
与装饰者模式区别:代理模式侧重于访问控制,装饰者模式侧重于功能动态附加分离,
作用场景
静态代理
实例
-
创建接口
public interface ITeachDao { void teach(); }
-
创建被代理对象
public class TeachDao implements ITeachDao { @Override public void teach() { System.out.println("teaching..."); } }
-
创建代理者,接收被代理对象
public class TeachDaoProxy implements ITeachDao { private final ITeachDao teachDao; public TeachDaoProxy(ITeachDao teachDao) { this.teachDao = teachDao; } @Override public void teach() { System.out.println("proxy on..."); teachDao.teach(); System.out.println("proxy off..."); } }
-
调用
public class ProxyTest {
public static void main(String[] args) {
TeachDaoProxy teachDaoProxy = new TeachDaoProxy(new TeachDao());
teachDaoProxy.teach();
}
}
输出结果
proxy on...
teaching...
proxy off...
优缺点
优点:
在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
缺点:
需要代理对象与目标对象实现一样接口,一旦接口增加方法,目标对象与代理对象都需要维护
###动态代理
介绍
JDK代理、接口代理
代理对象不需要实现接口,但目标哦对象要实现接口,否则不能使用动态代理
代理的对象生成利用JDK的api,动态的在内存中构建代理对象
实例
-
定义对象接口
public interface ITeachDao { void teach(); }
-
接口实现
public class TeachDao implements ITeachDao { @Override public void teach() { System.out.println("teaching..."); } }
-
定义代理工厂
public class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target; } public Object getProxyInstance(){ /** * public static Object newProxyInstance(ClassLoader loader, * Class<?>[] interfaces, * InvocationHandler h) { * ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法固定 * Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型 * InvocationHandler h: 事件处理,执行目标对象的方法时,触发事情处理器方法,将当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { /** * method方法对象 * args参数数组 **/ System.out.println(" jdk proxy on..."); Object invoke = method.invoke(target, args); System.out.println(" jdk proxy off..."); return invoke; }); } }
-
调用
public static void main(String[] args) { ITeachDao teachDao = new TeachDao(); ITeachDao proxyInstance = (ITeachDao) new ProxyFactory(new TeachDao()).getProxyInstance(); proxyInstance.teach(); }
输出结果
jdk proxy on...
teaching...
jdk proxy off...
Cglib代理
介绍
也叫子类代理,属于动态代理范畴,可以在内存中动态的创建对象,而不需要实现接口,需要引入cglib jar包
要求目标对象实现一个接口 ,若目标对象是一个单独对象,没有实现任何接口,可以使用目标对象子类实现代理
是在内存中构建一个子类对象从而实现对目标对象功能扩展,即Cglib代理归属到动态代理。
它是也强大的高性能代码生成器,可以在运行期扩展java类与实现java接口。广泛被许多AOP框架使用实现方法拦截
Cglib包底层通过使用字节码处理框架ASM来转换字节码并生成新的类
如何选择
- 目标对象需要实现接口,用JDK代理
- 目标对象不需要实现接口,用Cglib代理
实例
-
导入cglib 的jar包
-
定义目标对象
public class TeachDao{ public void teach() { System.out.println("teach..."); } }
-
创建代理工厂并实现cglib包的MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor { //维护一个目标对象 private final Object target; public ProxyFactory(Object target) { this.target = target; } //返回一个代理对象 public Object ProxyInstance(){ //创建一个工具类 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(target.getClass()); //设置回调函数 enhancer.setCallback(this); //创建子类对象并返回 return enhancer.create(); } //重写 调用目标对象的方法 @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("cglib proxy begin..."); Object invoke = method.invoke(target,objects); System.out.println("cglib proxy end..."); return invoke; } }
-
调用
public class CglibTest { public static void main(String[] args) { TeachDao teachDao = (TeachDao) new ProxyFactory(new TeachDao()).ProxyInstance(); teachDao.teach(); } }
输出结果
cglib proxy begin...
teach...
cglib proxy end...
代理模式变种
-
防火墙代理
内网通过代理穿透防火墙,实现对公网的访问
-
缓存代理
请求图片文件等资源时,先到缓存代理取,若取不到再到公网或者数据库取,然后缓存
-
远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象调用,远程代理通过网络和真正的远程对象沟通信息
-
同步代理
主要使用在多线程编程,完成多线程间同步工作
模板模式
概念
Template Method pattern 模板方法模式,在一个抽象类中公开定义执行它的方法模板。子类可以按需重写方法实现,但调用将以抽象类中定义的方法进行
模板方法模式定义一个操作的算法的骨架,将步骤延迟到子类中,使子类可以不改变一个算法的结构的基础上,就可以重定义该算法的某些特定步骤
属于行为型模式
作用场景
将共同的步骤/方法抽离出来
当要完成某个过程,该过程要执行一系列步骤,这一系列步骤基本相同,但个别步骤在实现时可能不同,通常考虑模板方法
实例
-
抽离抽象类定义固定步骤
public abstract class SoyMilk { final void make() { select(); add(); } final void select() { System.out.println("选材:"); } abstract void add(); }
-
定义具体类继承模板
public class RedBeanSoyMilk extends SoyMilk{ private final String str; public RedBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } }
public class BlackBeanSoyMilk extends SoyMilk{ private final String str; public BlackBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } }
-
调用
public class TemplateTest { public static void main(String[] args) { RedBeanSoyMilk redBeanSoyMilk = new RedBeanSoyMilk("红豆"); BlackBeanSoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk("黑豆"); redBeanSoyMilk.make(); blackBeanSoyMilk.make(); } }
输出结果
选材:
红豆
选材:
黑豆
钩子方法
介绍
在模板模式的父类中,定义一个方法。它默认不做任何事,子类可以根据情况选择是否覆盖,该方法称为钩子
根据钩子方法返回boolean 确定是否某步骤是否执行
实例
-
模板中添加钩子方法
public abstract class SoyMilk { final void make() { select(); //判断 if (customer()) add(); } final void select() { System.out.println("选材:"); } abstract void add(); //钩子,决定是否需要添加配料 boolean customer() { return true; } }
- 子类继承并重写
public class RedBeanSoyMilk extends SoyMilk { private final String str; public RedBeanSoyMilk(String str) { this.str = str; } @Override void add() { System.out.println(str); } //覆盖 @Override boolean customer() { return false; } }
-
调用
public class TemplateTest { public static void main(String[] args) { RedBeanSoyMilk redBeanSoyMilk = new RedBeanSoyMilk("红豆"); BlackBeanSoyMilk blackBeanSoyMilk = new BlackBeanSoyMilk("黑豆"); redBeanSoyMilk.make(); blackBeanSoyMilk.make(); } }
输出结果
选材:
选材:
黑豆
模板方法注意事项
- 基本思想:算法只存在于一个地方,也就是覆盖,容易修改。当需要修改算法时只需要修改父类的模板方法或已经实现的某些步骤。子类就会继承这些方法
- 实现最大化代码复用:父类的模板方法和已实现的某些睦州会被子类继承而直接使用
- 既统一了算法,也提供了很大灵活性,确保算法的结构保持不变,同时由子类提供部分步骤的实现
- 每个不同的实现都需要一个子类完成,类个数增加使系统庞大
- 一般模板方法都需要加上
final
关键字 防止子类重写模板方法
命令模式
概念
command pattern: 将一个请求封装成一个对象,以便使用不同参数表示不同的请求(命令),同时命令模式支持可撤销的操作
命令模式使得请求发送者与请求接收者消除彼此之间耦合,让对象之间的调用关系更加灵活,实现解耦
作用场景
界面的每个按钮都是一条命令、模拟cmd、订单的撤销\恢复、触发-反馈机制
实例
-
创建命令接收者
//命令接收者 public class LightReceiver { public void on() { System.out.println("light on.."); } public void off() { System.out.println("light off..."); } }
-
创建命令接口
//命令接口 public interface Command { //执行操作 void execute(); //撤销 void undo(); }
-
实现具体命令
//命令接收者,实现命令接口 public class LightOnCommand implements Command { private final LightReceiver lightReceiver; public LightOnCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.on(); } @Override public void undo() { lightReceiver.off(); } }
//命令接收者,实现命令接口 public class LightOffCommand implements Command { private final LightReceiver lightReceiver; public LightOffCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.off(); } @Override public void undo() { lightReceiver.on(); } }
/** * 没有任何命令,即空执行。用于初始化按钮,当调用空命令时,无忍耐和操作 * 这也是一种设计模式,省去对空判断 */ public class NoCommand implements Command{ @Override public void execute() { System.out.println("nothing..."); } @Override public void undo() { System.out.println("nothing..."); } }
-
创建命令调用者
//命令调用者 public class RemoteController { private final List<Command> onCommands; private final List<Command> offCommands; //执行撤销的命令 private Command undoCommand; public RemoteController() { onCommands = new ArrayList<>(); offCommands = new ArrayList<>(); initCommand(); } //初始化命令 private void initCommand() { for (int i = 0; i < 5; i++) { onCommands.add(new NoCommand()); offCommands.add(new NoCommand()); } } //插入角色 public void setCommand(int no, Command onCommand, Command offCommand) { onCommands.add(no, onCommand); offCommands.add(no, offCommand); } //按钮开按钮 public void onButtonWasPush(int no) { undoCommand = onCommands.get(no); undoCommand.execute(); } //按钮关按钮 public void offButtonWasPush(int no) { undoCommand = offCommands.get(no); undoCommand.execute(); } //撤销按钮 public void undoButtonWasPush() { if (undoCommand != null) { undoCommand.undo(); undoCommand = null; } else { System.out.println("nothing..."); } } }
-
调用
public class CommandTest { public static void main(String[] args) { RemoteController controller = new RemoteController(); LightReceiver lightReceiver = new LightReceiver(); LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver); controller.setCommand(0, lightOnCommand, lightOffCommand); controller.onButtonWasPush(0); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.offButtonWasPush(0); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.undoButtonWasPush(); System.out.println("--------"); controller.onButtonWasPush(2); } }
输出结果
light on..
--------
light off...
--------
light off...
--------
light on..
--------
nothing...
--------
nothing...
注意事项
- 核心:将发起请求的对象和执行请求的对象通过命令对象解耦,命令对象起到纽带桥梁作用
- 容易设计一个命令队列,只需要把命令对象放到队列,就可以多线程执行命令
- 容易实现对请求的撤销和重做
- 可能导致系统有过多的具体命令类,增加系统的复杂度
- 空命令是一种设计模式,省去了判空操作