目录
总起
- 面向对象(OOP)的七大原则
- 开闭原则: 对扩展开放,对修改关闭
- 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
- 依赖倒置原则: 要面向接口编程,不要面向实现编程。
- 单一职责原则: 控制类的粒度大小、将对象解耦、提高其内聚性。
- 接口隔离原则: 要为各个类建立它们需要的专用接口.
- 迪米特法则: 只与你的直接朋友交谈,不跟"陌生人”说话。
- 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
- 23种设计模式(GOF23)
-
创建型模式:
单例模式、 工厂模式、抽象工厂模式、建造者模式、原型模式。 -
结构型模式:
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
-
行为型模式:
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式, 访问者模式。
创建型模式
单例模式
单例模式有八种方式:
-
饿汉式(静态常量)
特点:可以避免线程同步问题,但可能造成内存浪费
public class SingletonTest01 {
public static void main(String[] args) {
// 调用返回都是同个实例对象
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2); // true
}
}
class Singleton{
// 类初始化时就实例化对象
private final static Singleton instance = new Singleton();
// 私有化构造器
private Singleton(){
}
// 提供公有的静态方法给外部调用,返回实例化对象
public static Singleton getInstance(){
return instance;
}
}
- 饿汉式(静态代码块)
public class SingletonTest02 {
public static void main(String[] args) {
// 调用返回都是同个实例对象
Singleton02 instance1 = Singleton02.getInstance();
Singleton02 instance2 = Singleton02.getInstance();
System.out.println(instance1 == instance2);
}
}
class Singleton02{
// 类初始化时就实例化对象
private static Singleton02 instance;
static { // 在静态代码块种实例化对象
instance = new Singleton02();
}
// 私有化构造器
private Singleton02(){
}
// 提供公有的静态方法给外部调用,返回实例化对象
public static Singleton02 getInstance(){
return instance;
}
}
- 懒汉式(线程不安全)
class Singleton03{
private static Singleton03 instance;
private Singleton03(){
}
public static Singleton03 getInstance(){
if (instance == null){ //未创建才创建
instance = new Singleton03();
}
return instance;
}
}
- 懒汉式(线程安全,同步方法)
上synchronized锁效率低,不推荐使用
class Singleton04{
private static Singleton04 instance;
private Singleton04(){
}
// 解决线程同步问题
public static synchronized Singleton04 getInstance(){
if (instance == null){ //未创建才创建
instance = new Singleton04();
}
return instance;
}
}
- 懒汉式(线程安全,同步代码块)
这种方法也不能起到线程同步的作用!!!可能在if判断发生同步问题!
拒绝使用这种方法~!!
class Singleton05{
private static Singleton05 instance;
private Singleton05(){
}
public static Singleton05 getInstance(){
if (instance == null){ //未创建才创建
synchronized (Singleton05.class){
instance = new Singleton05();
}
}
return instance;
}
}
- 双重检查
volatile 保证数据的可见性,让线程内存数据的变化立刻显示到主存中,而且有序性可以避免指令重排,但是不保证原子性。
class Singleton06{
private static volatile Singleton06 instance;
private Singleton06(){
}
public static Singleton06 getInstance(){
if (instance == null){ //未创建才创建
synchronized (Singleton06.class){
if (instance == null){
instance = new Singleton06();
}
}
}
return instance;
}
}
- 静态内部类(推荐使用)
外部类加载时,静态内部类不会立即加载,静态内部类只加载一次,线程安全。
JVM加载类的时候线程安全。
class Singleton07{
private static Singleton07 instance;
private Singleton07(){
}
private static class SingletonInstance{
private static final Singleton07 INSTANCE = new Singleton07();
}
public static Singleton07 getInstance(){
return SingletonInstance.INSTANCE;
}
}
- 枚举(可以避免反射机制的破坏)
推荐使用!
public class SingletonTest08 {
public static void main(String[] args) {
// 调用返回都是同个实例对象
Singleton08 instance1 = Singleton08.INSTANCE;
Singleton08 instance2 = Singleton08.INSTANCE;
System.out.println(instance1 == instance2);
}
}
enum Singleton08{
INSTANCE;
}
工厂模式
核心本质:
- 实例化对象不使用new,用工厂方法代替
- 将选择实现类, 创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
三种模式:
- 简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要球盖已有代码) - 工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品) - 抽象工厂模式
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
简单工厂模式(静态)
抽象接口
public interface Car {
void name();
}
各种具体实现类
public class BenChi implements Car {
@Override
public void name() {
System.out.println("奔驰车");
}
}
public class BMW implements Car {
@Override
public void name() {
System.out.println("宝马车");
}
}
工厂类:每次新增具体实现类都得添加代码,破坏了开闭原则
public class CarFactory {
public static Car getCar(String name){
switch (name){
case "奔驰":
return new BenChi();
case "宝马":
return new BMW();
default:
break;
}
return null;
}
}
调用
public class Consumer {
public static void main(String[] args) {
Car car1 = CarFactory.getCar("宝马");
Car car2 = CarFactory.getCar("奔驰");
car1.name();
car2.name();
}
}
工厂方法模式
每个抽象接口对应的具体实现类都有对应的工厂方法。
抽象接口
public interface Car {
void name();
}
抽象工厂接口
public interface CatFactory {
Car getCar();
}
各种具体实现类和其对应的工厂方法
奔驰车:
public class BenChi implements Car {
@Override
public void name() {
System.out.println("奔驰车");
}
}
public class BenChiFactory implements CatFactory {
@Override
public Car getCar() {
return new BenChi();
}
}
宝马车:
public class BMW implements Car {
@Override
public void name() {
System.out.println("宝马车");
}
}
public class BMWFactory implements CatFactory {
@Override
public Car getCar() {
return new BMW();
}
}
调用
public class Consumer {
public static void main(String[] args) {
Car car1 = new BenChiFactory().getCar();
Car car2 = new BMWFactory().getCar();
car1.name();
car2.name();
}
}
虽说简单工厂模式破坏面向对象的开闭原则,但是实际业务用的多的还是简单工厂模式,因为工厂方法模式的代码复杂不利用管理。
抽象工厂模式
- 优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
- 缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度
抽象产品族各接口
// 手机 小米手机 ,华为手机为同一产品等级结构
public interface IphoneProduct {
void start();
void shutdown();
}
// 路由器 小米路由器 ,华为路由器为同一产品等级结构
public interface RouterProduct {
void start();
void shutdown();
}
华为系列产品(同个产品族)
// 手机
public class HuaWeiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("华为手机开启");
}
@Override
public void shutdown() {
System.out.println("华为手机关闭");
}
}
// 路由器
public class HuaWeiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("华为路由器开启");
}
@Override
public void shutdown() {
System.out.println("华为路由器关闭");
}
}
小米系列产品(同个产品族)
// 手机
public class XiaoMIPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("小米手机开启");
}
@Override
public void shutdown() {
System.out.println("小米手机开启");
}
}
// 路由器
public class XiaoMiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("小米路由器开启");
}
@Override
public void shutdown() {
System.out.println("小米路由器关闭");
}
}
抽象产品工厂
// 抽象产品工厂
public interface ProductFactory {
// 生产手机
IphoneProduct iphoneProduct();
// 生产路由器
RouterProduct routerProduct();
}
各个产品族的产品工厂
// 小米工厂
public class XiaoMiFactory implements ProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new XiaoMIPhone();
}
@Override
public RouterProduct routerProduct() {
return new XiaoRouter();
}
}
// 华为工厂
public class HuaWeiFactory implements ProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new HuaWeiIPhone();
}
@Override
public RouterProduct routerProduct() {
return new HuaWeiRouter();
}
}
总结:不可以增加产品,但是可以增加产品族。
建造者模式
- 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
- 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
具体的产品对象
// 具体的产品
public class Product {
private String step1;
private String step2;
private String step3;
public String getStep1() {
return step1;
}
public void setStep1(String step1) {
this.step1 = step1;
}
public String getStep2() {
return step2;
}
public void setStep2(String step2) {
this.step2 = step2;
}
public String getStep3() {
return step3;
}
public void setStep3(String step3) {
this.step3 = step3;
}
@Override
public String toString() {
return "Product{" +
"step1='" + step1 + '\'' +
", step2='" + step2 + '\'' +
", step3='" + step3 + '\'' +
'}';
}
}
设置抽象的建造者
// 抽象的建造者
public abstract class Builder {
abstract void step1();
abstract void step2();
abstract void step3();
// 获得真正的建造产品
abstract Product getProduct();
}
具体的建造者继承抽象的建造者,实现抽象方法
// 具体的建造者
public class Worker extends Builder {
private Product product;
// 创建产品
public Worker(){
product = new Product();
}
@Override
void step1() {
product.setStep1("步骤1");
}
@Override
void step2() {
product.setStep1("步骤2");
}
@Override
void step3() {
product.setStep1("步骤3");
}
@Override
Product getProduct() {
return product;
}
}
核心:指挥者类,负责实例化创建对象,根据传入的具体建造者的对象,创建不同的具体对象,同时可以调整建造方法顺序
// 指挥者:指挥构建工程
public class Director {
public Product build(Builder builder){
// 指挥具体建造者进行对象建造
builder.step1();
builder.step2();
builder.step3();
return builder.getProduct();
}
}
测试
public class Test {
public static void main(String[] args) {
// 先创建一个具体建造者
Worker worker = new Worker();
// 创建一个指挥者,指挥具体建造者
Director director = new Director();
director.build(worker);
}
}
进一步优化,去掉指挥者,使用静态内部类,修改抽象建造者类,且具体产品赋予默认值
// 具体的产品
public class Product {
private String step1 = "步骤1";
private String step2 = "步骤2";
private String step3 = "步骤3";
public String getStep1() {
return step1;
}
public void setStep1(String step1) {
this.step1 = step1;
}
public String getStep2() {
return step2;
}
public void setStep2(String step2) {
this.step2 = step2;
}
public String getStep3() {
return step3;
}
public void setStep3(String step3) {
this.step3 = step3;
}
@Override
public String toString() {
return "Product{" +
"step1='" + step1 + '\'' +
", step2='" + step2 + '\'' +
", step3='" + step3 + '\'' +
'}';
}
}
// 抽象的建造者
public abstract class Builder {
abstract Builder step1(String msg);
abstract Builder step2(String msg);
abstract Builder step3(String msg);
// 获得真正的建造产品
abstract Product getProduct();
}
// 具体的建造者
public class Worker extends Builder {
private Product product;
// 创建产品
public Worker(){
product = new Product();
}
@Override
Builder step1(String msg) {
product.setStep1(msg);
return this;
}
@Override
Builder step2(String msg) {
product.setStep1(msg);
return this;
}
@Override
Builder step3(String msg) {
product.setStep1(msg);
return this;
}
@Override
Product getProduct(String msg) {
return product;
}
}
测试
public class Test {
public static void main(String[] args) {
// 创建一个具体建造者
Worker worker = new Worker();
Product product = Worker.getProduct(); // 使用设置的默认值
Product product = Worker.step1("步骤1").step2("步骤2").getProduct(); // 可以改变方法顺序,同时可以修改默认值
}
}
总结:
- 优点:
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的。
代码,符合"开闭原则"。
- 缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
原型模式
使用Object的clone方法进行克隆,bean对象实现Cloneable接口,实现父类的clone()方法,里面有方法引用的需要进行属性克隆。
结构型模式
适配器模式
定义:将一个类的接口转换成客户希望的另外-一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作!
角色分析:
- 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
- 需要适配的类:需要适配的类或适配者类。
- 适配器: 通过包装-一个需要适配的对象,把原接口转换成目标对象! .
想要调用的目标类
public class Adaptee {
public void connect(){
System.out.println("连接");
}
}
适配器接口
// 接口转换器的抽象接口
public interface NetToUsb {
// 处理转换
public void handleRequest();
}
适配器实现类
// 真正的适配器
// 1. 继承(类适配器,单一继承)
//public class Adapter extends Adaptee implements NetToUsb{
//
// @Override
// public void handleRequest() {
// super.connect();
// }
//}
// 2. 组合(对象适配器,多适配)
public class Adapter implements NetToUsb{
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.connect();
}
}
需要适配的类
public class Computer {
public void internet(NetToUsb adapter){
adapter.handleRequest();
}
}
测试
public static void main(String[] args) {
Computer computer = new Computer(); // 需要适配的类
Adaptee adaptee = new Adaptee(); // 适配的对象类
Adapter adapter = new Adapter(adaptee); // 适配器类
computer.internet(adapter); // 调用适配对象的方法
}
-
对象适配器优点:
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一 个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
-
类适配器缺点:
- 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 在Java、 C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
-
应用场景:
- 适用场景系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
桥接模式
品牌抽象接口
// 品牌
public interface Brand {
void info();
}
各具体品牌类
public class Apple implements Brand {
@Override
public void info() {
System.out.println("苹果");
}
}
public class Lenvo implements Brand {
@Override
public void info() {
System.out.println("联想");
}
}
抽象的电脑类型类
// 抽象的电脑类型类
public abstract class Computer {
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info(){
brand.info();
}
}
具体的电脑类型实现类
class Desktop extends Computer{
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
class Laptop extends Computer{
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本");
}
}
测试组装起来
public class Test {
public static void main(String[] args) {
// 苹果笔记本
Computer computer1 = new Laptop(new Apple());
computer1.info();
// 联想台式机
Computer computer2 = new Desktop(new Lenvo());
computer2.info();
}
}
好处分析:
-
桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
-
桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度, 都不需要修改原
有系统。符合开闭原则,就像- -座桥,可以把两个变化的维度连接起来!
劣势分析:
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
使用的场景:
- Java语言通过Java虚拟机实现了平台的无关性。
- AWT中的Peer架构
- JDBC驱动程序也是桥接模式的应用之一。
总结:如果一个类存在两个独立变化的维度,且这两个维度都需要进行扩展,或者不希望使用继承或者多层级继承导致类数量变多的系统,就可使用桥接模式。特别可以让程序同时适用Linux,window等系统时可以使用该模式。
装饰者模式
装饰者模式: 动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
public class Test {
public static void main(String[] args) {
DataInputStream dis = new DataInputStream(new FileInputStream("c:\\abc.txt"));
System.out.println(dis.read());
dis. close();
}
}
IO流中的InputStream 是抽象类,为被装饰者,
FileInputStream 是 InputStream的子类,
FilterInputStream 是 InputStream 子类,作为修饰者,
DataInputStream 是 FilterInputStream子类,作为具体的修饰者
FilterInputStream类有 protected volatile InputStream in; 即含被装饰者
组合模式
基本介绍:
- 组合模式(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 organizationComponent){
// 默认不能操作
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent){
// 默认不能操作
throw new UnsupportedOperationException();
}
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;
}
// 子类都需实现
protected abstract void print();
}
大学类
public class University extends OrganizationComponent{
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public University(String name, String des) {
super(name, des);
}
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
protected void print() {
System.out.println(getName());
for (OrganizationComponent organizationComponent:organizationComponents){
organizationComponent.print();
}
}
}
学院类
public class College extends OrganizationComponent{
// List 存放department
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public College(String name, String des) {
super(name, des);
}
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
protected void print() {
System.out.println(getName());
for (OrganizationComponent organizationComponent:organizationComponents){
organizationComponent.print();
}
}
}
最底层的叶子节点类–>系
public class Department extends OrganizationComponent {
public Department(String name, String des) {
super(name, des);
}
@Override
protected void print() {
System.out.println(getName());
}
}
测试
public class Client {
public static void main(String[] args) {
// 从高到低
// 大学
OrganizationComponent university = new University("北京大学", "666");
// 学院
OrganizationComponent college1 = new College("计算机学院", "999");
OrganizationComponent college2 = new College("金融学院", "555");
// 系
college1.add(new Department("软件工程", "666"));
college1.add(new Department("网格工程", "666"));
college2.add(new Department("国贸专业", "999"));
college2.add(new Department("商贸专业", "999"));
// 学院加入到大学中
university.add(college1);
university.add(college2);
university.print();
college1.print();
}
}
外观模式
基本介绍:
- 外观模式(Facade) ,也叫“过程模式:外观模式为子系统中的一组接口提供
一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 - 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端
只需跟这个接口发生调用,而无需关心这个子系统的内部细节。
基本关系:
- 外观类(Facade):为调用端提供统-的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象。
- 调用者(Client) :外观接口的调用者。
- 子系统的集合:指模块或者子系统,处理Facade对象指派的任务,他是功能的提供者。
子系统类TV
public class TV {
private final static TV instance = new TV();
private TV() {
}
public static TV getInstance(){
return instance;
}
public void on(){
System.out.println("电视开启");
}
public void off(){
System.out.println("电视关闭");
}
}
子系统Player
public class Player {
private final static Player instance = new Player();
private Player() {
}
public static Player getInstance(){
return instance;
}
public void on(){
System.out.println("音乐开启");
}
public void off(){
System.out.println("音乐关闭");
}
}
外观模式管理器类
public class Facade {
private TV tv;
private Player player;
public Facade() {
this.tv = TV.getInstance();
this.player = Player.getInstance();
}
// 全部开启
public void ready(){
tv.on();
player.on();
}
// 全部关闭
public void end(){
player.off();
tv.off();
}
}
测试
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.ready();
facade.end();
}
}
享元模式
基本介绍:
- 享元模式(Flyweight Pattern)也叫蝇量模式:运
用共享技术有效地支持大量细粒度的对象 - 常用于系统底层开发,解决系统的性能问题。
像数据库连接池,里面都是创建好的连接对象,在
这些连接对象中有我们需要的则直接拿来用,避
免重新创建,如果没有我们需要的,则创建一个。 - 享元模式能够解决重复对象的内存浪费的问题,
当系统中有大量相似对象,需要缓冲池时。不需
总是创建新对象,可以从缓冲池里拿。这样可以
降低系统内存,同时提高效率。
网站抽象类
public abstract class WebSite {
public abstract void use(User user);
}
具体实现类(可共享的内部状态)
public class ConcreteWebSite extends WebSite{
// 内部状态 可以共享
private String type = "";// 网站类型
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
System.out.println("网站的发布形式为:"+type+",使用的用户是:"+user.getName());
}
}
用户对象为外部状态不可共享
// 作为外部状态,不能共享
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 WebSiteFactory {
// 池集合
private HashMap<String,ConcreteWebSite> pool = new HashMap<>();
// 根据类型返回一个网站,如果没有该类型就创建一个网站,并放入池集合进行管理
public WebSite getWebSite(String type){
if (!pool.containsKey(type)){
pool.put(type,new ConcreteWebSite(type));
}
return pool.get(type);
}
// 获取池集合中有多个类型的网站
public int getWebSiteNum(){
return pool.size();
}
}
测试
public class Client {
public static void main(String[] args) {
WebSiteFactory webSiteFactory = new WebSiteFactory();
WebSite webSite1 = webSiteFactory.getWebSite("新闻");
webSite1.use(new User("111"));
WebSite webSite2 = webSiteFactory.getWebSite("博客");
webSite2.use(new User("222"));
WebSite webSite3 = webSiteFactory.getWebSite("博客");
webSite3.use(new User("333"));
System.out.println("网站的实际类型数量:"+webSiteFactory.getWebSiteNum());
}
}
附加例子:jdk中的Integer的valueof产生的对象, 数值如果是-128~127之间的对象是相同的,缓存池的作用。
代理模式
基本介绍:
- 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理
对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的
功能操作,即扩展目标对象的功能。 - 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
- 代理模式有不同的形式,主要有三种静态代理、动态代理(JDK代理,接口代理)和Cglib代理。
静态代理
角色分析:
- 抽象角色: 一般会使用接口或者抽象类来解决
- 真实角色: 被代理的角色
- 代理角色: 代理真实角色,代理真实角色后,我们一般会做- -些附属操作
- 客户: 访问代理对象的人!
抽象角色类
public interface ITeacherDao {
void teach();
}
真实角色类
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("授课中~");
}
}
代理角色类
// 代理类
public class TeacherProxy implements ITeacherDao {
// 目标类
private ITeacherDao teacherDao;
public TeacherProxy(ITeacherDao teacherDao) {
this.teacherDao = teacherDao;
}
@Override
public void teach() {
System.out.println("代理中~");
teacherDao.teach();
}
}
客户类
public class Client {
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
TeacherProxy teacherProxy = new TeacherProxy(teacherDao);
teacherProxy.teach();
}
}
优点:
- 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
缺点:
- 因为代理对象需要与目标对象实现- -样的接口,所以会有很多代理类。
- 一旦接口增加方法,目标对象与代理对象都要维护。
动态代理
基本介绍:
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API, 动态的在内存中构建代理对象
- 动态代理也叫做: JDK代理、 接口代理
抽象角色类
public interface ITeacherDao {
void teach();
void write(String msg);
}
真实角色类
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("授课中~");
}
@Override
public void write(String msg) {
System.out.println("写了"+msg);
}
}
代理角色类
// 代理类
public class TeacherProxy implements ITeacherDao {
// 目标类
private Object target;
public TeacherProxy(ITeacherDao target) {
this.target = target;
}
public Object getProxyInstance() {
// loader:指定当前目标对象使用的类加载器,获取加载器的方法固定
// interfaces:目标对象实现的接口类型,使用泛型确认类型
// InvocationHandler: 事件处理,执行目标对象的方法时,会触发事件处理器方法,会把当前执行对象的方法作为参数传入
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理开始");
Object result = method.invoke(target, args);
System.out.println("代理结束");
return result;
}
});
}
}
客户类
public class Client {
public static void main(String[] args) {
// 创建目标对象
ITeacherDao teacherDao = new TeacherDao();
// 给目标对象创建代理对象,转成接口
ITeacherDao teacherProxy = (ITeacherDao) new TeacherProxy(teacherDao).getProxyInstance();
// 调用目标对象的方法
teacherProxy.teach();
teacherProxy.write("测试");
}
}
Cglib代理
与动态代理的区别,目标对象不用实现任何接口,需要导入Cglib的相关jar包才能实现。
- TeacherDao不用实现任何接口;
- ProxyFactory类中的getProxyInstacne()方法给目标对象target创建一个代理对象重写intecept方法,实现对被代理对象(目标对象)方法的调用
目标对象类
public class TeacherDao{
public void teach() {
System.out.println("授课中~");
}
}
代理类
// 代理类
public class TeacherProxy implements MethodInterceptor {
// 目标类
private Object target;
public TeacherProxy(ITeacherDao target) {
this.target = target;
}
// 为目标对象的代理对象
public Object getProxyInstance() {
//1.创建一个工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(target.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即代理对象
return enhancer.create();
}
//重写intercept方法,会调用目标对象的方法
@Override
public 0bject intercept(object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("代理开始");
Object result = method.invoke(target,arg2);
System.out.println("代理结束");
return result;
}
}
客户类测试
public class Client {
public static void main(String[] args) {
// 创建目标对象
TeacherDao target = new TeacherDao();
// 给目标对象创建代理对象,转成接口
TeacherDao teacherProxy = (ITeacherDao) new TeacherProxy(target).getProxyInstance();
// 执行代理对象的方法,触发拦截器方法,从而调用目标对象的方法
teacherProxy.teach();
}
}
行为型模式
模板模式
抽象模板类
public abstract class SoyaMilk {
// 模板方法
final void make(){
select();
if (isAddOther()) {
addOther();
}
cook();
}
void select(){
System.out.println("选材");
}
abstract void addOther();
void cook(){
System.out.println("烹煮");
}
// 钩子方法,默认添加
boolean isAddOther(){
return true;
};
}
各种具体类
public class PeanutSoya extends SoyaMilk {
@Override
void addOther() {
System.out.println("加入花生");
}
}
public class RedSoya extends SoyaMilk {
@Override
void addOther() {
System.out.println("加入红豆");
}
}
public class PureSoya extends SoyaMilk {
// 表示不添加别的东西
@Override
boolean isAddOther(){
return false;
};
}
测试调用
public class Client {
public static void main(String[] args) {
// 制作红豆豆浆
SoyaMilk redSoya = new RedSoya();
redSoya.make();
// 制作花生豆浆
SoyaMilk peanutSoya = new PeanutSoya();
peanutSoya.make();
}
}
命令模式
基本介绍:
- 命令模式(Command Pattern) :在软件设计中,我们经常需要
向某些对象发送请求,但是并不知道请求的接收者是谁,也不知
道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计 - 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让
对象之间的调用关系更加灵活,实现解耦。 - 在命名模式中,会将一个请求封装为一个对象,以便使用不同参
数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
- Invoker是调用者角色
- Command:是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
- Receiver:接受者角色,知道如何实施和执行个请求相关的操作
- ConereteCommand:将一个接受者对象与一个动作绑定,调用接受者相应的操
作,实现execute
抽象命令类
public interface Command {
public void execute();
public void undo();
}
电灯对象(命令接收者)
public class LightReceiver {
public void on(){
System.out.println("开启");
}
public void off(){
System.out.println("关闭");
}
}
电灯开的类
public class LightOnCommand implements Command {
private 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 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() {
}
@Override
public void undo() {
}
}
控制器(命令发送者)
public class RemoteController {
Command[] onCommandArr;
Command[] offCommandArr;
// 撤销命令
Command undoCommand;
// 初始化命令
public RemoteController(){
undoCommand = new NoCommand();
onCommandArr = new Command[5];
offCommandArr = new Command[5];
for (int i =0 ;i<5;i++){
onCommandArr[i] = new NoCommand();
offCommandArr[i] = new NoCommand();
}
}
public void setCommand(int num,Command onCommand,Command offCommand){
onCommandArr[num] = onCommand;
offCommandArr[num] = offCommand;
}
// 按下开
public void onOpen(int num){
onCommandArr[num].execute();
// 同时记录操作
undoCommand = onCommandArr[num];
}
// 按下关
public void offEnd(int num){
offCommandArr[num].execute();
// 同时记录操作
undoCommand = offCommandArr[num];
}
// 按下撤销操作
public void unDoCommand(){
undoCommand.undo();
}
}
用户测试类
public class Client {
public static void main(String[] args) {
// 电灯对象
LightReceiver lightReceiver = new LightReceiver();
// 电灯开和关对象
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 创建一个控制对象
RemoteController remoteController = new RemoteController();
// 给控制对象设置命令
remoteController.setCommand(0, lightOnCommand,lightOffCommand);
remoteController.onOpen(0); // 开
remoteController.offEnd(0); // 关
remoteController.unDoCommand(); // 撤销上一步操作
}
}
注意点:
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要
调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对
象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”
请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到
了纽带桥梁的作用。 - 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
- 容易实现对请求的撤销和重做
- 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这
点在在使用的时候要注意 - 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没
有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定 的麻烦。 - 命令模式经典的应用场景:界面的-一个按钮都是一条命令、 模拟CMD (DOS命令)
订单的撤销/恢复、触发-反馈机制
访问者模式
基本介绍:
- 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,
它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 - 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
- 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
- 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作
(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以
选用访问者模式解决
访问动作抽象类
public abstract class action {
public abstract void getManResult(Man man);
public abstract void getWomanResult(Woman woman);
}
动作具体实现类
public class Success extends action {
@Override
public void getManResult(Man man) {
System.out.println("男成功");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女成功");
}
}
public class Fail extends action {
@Override
public void getManResult(Man man) {
System.out.println("男失败");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女失败");
}
}
被访问者抽象类
public abstract class Person {
public abstract void accept(action action);
}
被访问者具体实现类
public class Man extends Person {
@Override
public void accept(action action) {
action.getManResult(this);
}
}
public class Woman extends Person {
@Override
public void accept(action action) {
action.getWomanResult(this);
}
}
管理者类
public class ObjectStructure {
private List<Person> persons = new LinkedList<>();
public void attach(Person person){
persons.add(person);
}
public void detach(Person person){
persons.remove(person);
}
public void display(action action){
for (Person p:persons){
p.accept(action);
}
}
}
测试类
public class Client {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
Success success = new Success();
objectStructure.display(success);
}
}
优点:
- 访问者模式符合单 --职责原则、让程序具有优秀的扩 展性、灵活性非常高。
- 访问者模式可以对功能进行统一,可以做报表、UI、 拦截器与过滤器,适用于数据
结构相对稳定的系统。
缺点:
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米
特法则所不建议的,这样造成了具体元素变更比较困难 - 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此, 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么问者模式就是比较合适的。
迭代器模式
基本介绍:
- 迭代器模式( Iterator Pattern)是常用的设计模式,属于行为型模式
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,
或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历
方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。 - 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,
不需要知道集合对象的底层表示,即:不暴露其内部的结构。
学院抽象类
public interface College {
public String getName();
public void addDepartment(String name,String des);
public Iterator createIterator();
}
学院具体实现类
public class CpCollege implements College {
Department[] departments;
int num = 0;
public CpCollege() {
departments = new Department[5];
addDepartment("软件工程", "666");
addDepartment("网络工程", "666");
}
@Override
public String getName() {
return "计算机学院";
}
@Override
public void addDepartment(String name,String des) {
Department department = new Department(name, des);
departments[num] = department;
num++;
}
@Override
public Iterator createIterator() {
return new CpCollegeIterator(departments);
}
}
自定义的学院迭代器
public class CpCollegeIterator implements Iterator {
Department[] departments;
int pos = 0;
public CpCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (pos>=departments.length|| departments[pos]==null){
return false;
}
return true;
}
@Override
public Object next() {
Department department = departments[pos];
pos++;
return department;
}
@Override
public void remove() {
}
}
迭代输出类
public class OutPutImpl {
List<College> collegeList;
public OutPutImpl(List<College> collegeList) {
this.collegeList = collegeList;
}
public void printCollege(){
Iterator<College> iterator = collegeList.iterator();
while (iterator.hasNext()){
College college = iterator.next();
System.out.println(college.getName());
printDepartment(college.createIterator());
}
}
public void printDepartment(Iterator iterator){
while (iterator.hasNext()){
Department d = (Department) iterator.next();
System.out.println(d.getName());
}
}
}
测试
public class Client {
public static void main(String[] args) {
ArrayList<College> colleges = new ArrayList<>();
CpCollege cpCollege = new CpCollege();
colleges.add(cpCollege);
OutPutImpl outPut = new OutPutImpl(colleges);
outPut.printCollege();
}
}
优点:
-
提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以
遍历对象了。 -
隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚
合的具体组成。 -
提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任
原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集
合的责任分开,这样-来集合改变的话,只影响到聚合对象。而如果遍历方式改变
的话,只影响到了迭代器。 -
当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式
缺点:
- 每个聚合对象都要-一个迭代器, 会生成多个迭代器不好管理类。
观察者模式
观察者模式: 对象之间多对一-依赖的一 种设计方案,被依赖的对象为Subject,依赖的对象为Observer, Subject通知Observer变化。
优点:
- 观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
- 这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。
Subject类:首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
public class Subject {
//观察者数组
private Vector<Observer> vector = new Vector<>();
//增加一个观察者
public void addObserver(Observer observer) {
vector.add(observer);
}
//删除一个观察者
public void deleteObserver(Observer observer) {
vector.remove(observer);
}
//通知所有观察者
public void notifyObserver() {
for(Observer observer : vector) {
observer.update();
}
}
}
抽象观察者Observer类:观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。
public interface Observer {
//更新数据
public void update();
}
具体主题子类:继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
public class ConcreteSubject extends Subject {
//具体业务操作
public void doSomething() {
//相关操作。。。
super.notifyObserver();
}
}
具体观察者:实现Observer接口。
public class ConcreteObserver implements Observer {
@Override
public void update() {
// 接受主题的消息。
}
}
客户端测试类
public class Client {
public static void main(String[] args) {
//创建一个主题
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer observer = new ConcreteObserver();
//将观察者添加到主题中
subject.addObserver(observer);
//开始活动
subject.doSomething();
}
}
中介者模式
基本介绍:
- 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 中介者模式属于行为型模式,使代码易于维护
- 比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用
-
Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
-
Colleague 是抽象同事类
-
ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个同事对象消息,完成相应的任务
-
ConcreteColleague 具体的同事类,会有很多, 每个同事只知道自己的行为, 而不了解其他同事类的行为(方法), 但 是他们都依赖中介者对象
抽象同事类
public abstract class Colleague {
private Mediator mediator;
public String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public Mediator GetMediator() {
return this.mediator;
}
public abstract void SendMessage(int stateChange);
}
具体同事类
public class Alarm extends Colleague {
//构造器
public Alarm(Mediator mediator, String name) {
super(mediator, name);
//在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合]
mediator.Register(name, this);
}
public void SendAlarm(int stateChange) {
SendMessage(stateChange);
}
@Override
public void SendMessage(int stateChange) {
// 调 用 的 中 介 者 对 象 的 getMessage
this.GetMediator().GetMessage(stateChange, this.name);
}
}
抽象中介类
public abstract class Mediator {
//将给中介者对象,加入到集合中
public abstract void Register(String colleagueName, Colleague colleague);
//接收消息, 具体的同事对象发出
public abstract void GetMessage(int stateChange, String colleagueName);
public abstract void SendMessage();
}
具体的中介者类
public class ConcreteMediator extends Mediator {
//集合,放入所有的同事对象
private HashMap<String, Colleague> colleagueMap;
private HashMap<String, String> interMap;
public ConcreteMediator() {
colleagueMap = new HashMap<String, Colleague>();
interMap = new HashMap<String, String>();
}
@Override
public void Register(String colleagueName, Colleague colleague) {
colleagueMap.put(colleagueName, colleague);
if (colleague instanceof Alarm) {
interMap.put("Alarm", colleagueName);
}else if(..){
...
}
}
//1. 根据得到消息,完成对应任务
//2. 中介者在这个方法,协调各个具体的同事对象,完成任务
@Override
public void GetMessage(int stateChange, String colleagueName) {
//处理闹钟发出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) {
.....
} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗帘发出的消息,这里处理...
}
}
@Override
public void SendMessage() {
}
}
优点:
- 多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
- 减少类间依赖,降低了耦合,符合迪米特原则
缺点:
- 中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
- 如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
备忘录模式
基本介绍:
- 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
- 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
人物初始值类
public class Originator {
private String state; // 状态
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
// 返回一个状态对象
public Memento SaveMemento(){
return new Memento(state);
}
//通过备忘录对象,恢复状态
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
状态类
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
备忘录对象类
public class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento memento){
mementoList.add(memento);
}
public Memento get(int index){
return mementoList.get(index);
}
}
测试
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("1");
// 保存当前状态1
caretaker.add(originator.SaveMemento());
originator.setState("2");
// 保存当前状态2
caretaker.add(originator.SaveMemento());
// 从状态2恢复到状态1
System.out.println("当前状态:"+originator.getState());
//originator.setState(caretaker.get(0).getState());
originator.getStateFromMemento(caretaker.get(0));
System.out.println("恢复状态:"+originator.getState());
}
}
注意点:
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
-
实现了信息的封装,使得用户不需要关心状态的保存细节。
-
如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意。
-
适用的应用场景:
1、后悔药。
2、打游戏时的存档。
3、Windows 里的 ctri + z。
4、IE 中的后退。
5、数据库的事务管理
- 为了节约内存,备忘录模式可以和原型模式配合使用。
解释器模式
基本介绍:
-
在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
-
解释器模式(Interpreter Pattern):是指给定一个语言**(表达式),定义它的文法的一种表示,并定义一个解释器, 使用该解释器来解释语言中的句子(**表达式)
-
应用场景
-
应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
-
一些重复出现的问题可以用一种简单的语言来表达
-
一个简单语法需要解释的场景
-
这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等
-
Context: 是环境角色,含有解释器之外的全局信息.
-
AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
-
TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
-
NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作.
-
说明: 输入 Context he TerminalExpression 信息通过 Client 输入即可
解释器封装类
public class Calculator {
//定义表达式
private Expression expression;
//构造函数传参,并解析
public Calculator(String expStr) {
//安排运算先后顺序
Stack<Expression> stack = new Stack<>();
//表达式拆分为字符数组
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
for(int i=0; i<charArray.length; i++) {
switch (charArray[i]) {
case '+': //加法
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left, right));
break;
case '-': //减法
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default: //公式中的变量
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
this.expression = stack.pop();
}
//计算
public int run(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}
抽象表达式类
public abstract class Expression {
//解析公式和数值,key是公式中的参数,value是具体的数值
public abstract int interpreter(HashMap<String, Integer> var);
}
变量解析器
public class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
// var 为存的变量与值的键值对
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}
运算符号解析器
public class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
// 由子类实现
@Override
public int interpreter(HashMap<String, Integer> var) {
// TODO Auto-generated method stub
return 0;
}
}
加法解析器
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
减法解析器
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
测试
public class Client {
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
HashMap<String, Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
//获得表达式
public static String getExpStr() throws IOException {
System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
//获得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for(char ch : expStr.toCharArray()) {
if(ch != '+' && ch != '-' ) {
if(! map.containsKey(String.valueOf(ch))) {
System.out.print("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
状态模式
基本介绍:
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
-
Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
-
State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
-
ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为
Context类
环境角色具有两个职责,即处理本状态必须完成的任务,及决定是否可以过渡到其它状态。对于环境角色,有几个不成文的约束:
- 即把状态对象声明为静态常量,有几个状态对象就声明几个状态常量
- 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式
public class Context {
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//当前状态
private State currentState;
//获得当前状态
public State getCurrentState() {
return currentState;
}
//设置当前状态
public void setCurrentState(State currentState) {
this.currentState = currentState;
//切换状态
this.currentState.setContext(this);
}
public void handle1() {
this.currentState.handle1();
}
public void handle2() {
this.currentState.handle2();
}
}
State抽象类
抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。
public abstract class State {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
//行为1
public abstract void handle1();
//行为2
public abstract void handle2();
}
具体状态
public class ConcreteState1 extends State {
@Override
public void handle1() {
//...
System.out.println("ConcreteState1 的 handle1 方法");
}
@Override
public void handle2() {
super.context.setCurrentState(Context.STATE2);
System.out.println("ConcreteState1 的 handle2 方法");
}
}
public class ConcreteState2 extends State {
@Override
public void handle1() {
//...
System.out.println("ConcreteState2 的 handle1 方法");
}
@Override
public void handle2() {
super.context.setCurrentState(Context.STATE2);
System.out.println("ConcreteState2 的 handle2 方法");
}
}
测试
public class Client {
public static void main(String[] args) {
//定义环境角色
Context context = new Context();
//初始化状态
context.setCurrentState(new ConcreteState1());
//行为执行
context.handle1();
context.handle2();
}
}
注意细节:
-
代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
-
方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错。
-
符合“开闭原则”。容易增删状态。
-
会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度。
-
应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式。
策略模式
基本介绍:
- 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
- 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
Context:上下文角色,也叫Context封装角色,起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
Strategy :抽象策略角色,是对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性
ConcreteStrategy:用于实现抽象策略中的操作,即实现具体的算法,不同策略不同类。
Context类(上下文角色)
public class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
//上下文接口
public void contextInterface() {
strategy.algorithmInterface();
}
}
Strategy抽象类(策略角色)
public abstract class Strategy {
//算法方法
public abstract void algorithmInterface();
}
具体策略角色
public class ConcreteStrategyA extends Strategy {
@Override
public void algorithmInterface() {
System.out.println("算法A实现");
}
}
public class ConcreteStrategyB extends Strategy {
@Override
public void algorithmInterface() {
System.out.println("算法B实现");
}
}
测试
public class Client {
public static void main(String[] args) {
Context context;
// 设置策略
context = new Context(new ConcreteStrategyA());
context.contextInterface();
// 设置修改策略
context = new Context(new ConcreteStrategyB());
context.contextInterface();
}
}
策略模式的应用:
抽象鸭子类
public abstract class Duck {
//属性, 策略接口
FlyBehavior flyBehavior;
public Duck() {
}
public abstract void display();//显示鸭子信息
public void quack() {
System.out.println("鸭子嘎嘎叫~~");
}
public void swim() {
System.out.println("鸭子会游泳~~");
}
public void fly() {
//改进
if(flyBehavior != null) {
flyBehavior.fly();
}
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
}
飞的接口
public interface FlyBehavior {
void fly(); // 子类具体实现
}
实现会飞的接口行为
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("会飞");
}
}
实现不会飞的接口行为
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("不会飞翔");
}
}
实现的鸭子类(会飞)
public class PekingDuck extends Duck {
//假如北京鸭会飞。
public PekingDuck() {
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
System.out.println("~~北京鸭~~~");
}
}
实现的鸭子类(玩具鸭,不会飞,不会游泳)
public class ToyDuck extends Duck{
public ToyDuck() {
flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
System.out.println("玩具鸭");
}
//需要重写父类的所有方法
public void quack() {
System.out.println("玩具鸭不能叫~~");
}
public void swim() {
System.out.println("玩具鸭不会游泳~~");
}
}
客户端测试
public class Client {
public static void main(String[] args) {
ToyDuck toyDuck = new ToyDuck();
toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck();
pekingDuck.fly();
//动态改变某个对象的行为, 北京鸭 不能飞
pekingDuck.setFlyBehavior(new NoFlyBehavior());
System.out.println("北京鸭的实际飞翔能力");
pekingDuck.fly();
}
}
注意细节:
-
策略模式的关键是:分析项目中变化部分与不变部分
-
策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
-
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
-
提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
-
需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
职责链模式
基本介绍:
- 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
- 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
类图
-
Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
-
ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
-
Request , 含义很多属性,表示一个请求
具体例子:
客户请求类
// 客户请求类
public class PurchaseRequest {
private int type = 0; // 类型
private float price = 0.0f; // 价格
private int id = 0; // id
public PurchaseRequest(int type, float price, int id) {
this.type = type;
this.price = price;
this.id = id;
}
public int getType() {
return type;
}
public float getPrice() {
return price;
}
public int getId() {
return id;
}
}
抽象处理者
public abstract class Approver {
Approver approver; // 下一个处理者
String name;
public Approver(String name) {
this.name = name;
}
// 设置下一个处理的人
public void setApprover(Approver approver) {
this.approver = approver;
}
// 处理请求的方法,由子类完成
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
具体处理者
// 系处理
public class DepartmentApprover extends Approver {
public DepartmentApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()<=500){
System.out.println("请求id:"+purchaseRequest.getId()+",本次请求由系【"+this.name+"】处理了");
}else{
// 不行就由下一个处理者处理
this.approver.processRequest(purchaseRequest);
}
}
}
// 院处理
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()>500 && purchaseRequest.getPrice()<=1000){
System.out.println("请求id:"+purchaseRequest.getId()+",本次请求由院【"+this.name+"】处理了");
}else{
// 不行就由下一个处理者处理
this.approver.processRequest(purchaseRequest);
}
}
}
// 校处理
public class SchoolApprover extends Approver {
public SchoolApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice()>1000){
System.out.println("请求id:"+purchaseRequest.getId()+",本次请求由校【"+this.name+"】处理了");
}else{
// 最后不能处理就报错
throw new UnsupportedOperationException("无法处理该请求!");
}
}
}
测试
public class Client {
public static void main(String[] args) {
PurchaseRequest purchaseRequest = new PurchaseRequest(1,1200,0);
// 创建各个处理者
DepartmentApprover departmentApprover = new DepartmentApprover("张系长");
CollegeApprover collegeApprover = new CollegeApprover("黄院长");
SchoolApprover schoolApprover = new SchoolApprover("刘校长");
// 设置各个处理者的下一位兜底处理者,设置环状处理
departmentApprover.setApprover(collegeApprover);
collegeApprover.setApprover(schoolApprover);
schoolApprover.setApprover(departmentApprover);
departmentApprover.processRequest(purchaseRequest);
}
}
注意细节:
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat
对 Encoding 的处理、拦截器