一、结构型模式:
--核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
--分类:适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式
1.适配器模式(adapter)
1)什么是适配器?
将一个类的接口转换成客户希望的另一个接口。
Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
2)模式中的角色
-目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
-需要适配的类(Adaptee):需要适配的类或适配者的类
-适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
/* * 被适配的类 * 相当于例子中的,PS/2键盘 * * */ public class Adaptee { public void request() { System.out.println("可以完成客户的需求"); } }
* 客户端类 * 相当于例子中的笔记本,只有USB接口 * */ public class Client { public void test1(Target target) { target.handleRequest(); } public static void main(String[] args) { Client c = new Client(); Adaptee a = new Adaptee(); Target t = new Adapter2(a); c.test1(t); } }
public interface Target { void handleRequest(); }
/* * 适配器(类适配方式) * 相当于usb和ps/2的转接器 * */ public class Adapter extends Adaptee implements Target { @Override public void handleRequest() { super.request(); } }
/* * 适配器(对象适配器方式,使用了组合的方式跟被适配对象整合) * 相当于usb和ps/2的转接器 * */ public class Adapter2 implements Target { private Adaptee adaptee; public Adapter2(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void handleRequest() { adaptee.request(); } }
2.代理模式
--核心作用:
通过代理,控制对对象的访问
通过详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。
(即AOP的微观实现)AOP(Aspect Oriented Programming 面向切面编程)的核心机制
--核心角色
1)抽象角色:定义代理角色和真实角色的公共对外方法
2)真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用 关注真正的业务逻辑
3)代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
将统一的流程控制放到代理角色中处理
--应该场景
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法的调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档有大的图片,有可能一个图片有100MB,在打开文件不可能将所有图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
--分类
1)静态代理(静态定义代理类)
public interface Star { //面谈 void confer(); //签合同 void signContract(); //订票 void bookTicket(); //唱歌 void sing(); //收钱 void collectMoney(); }
public class RealStar implements Star { @Override public void confer() { System.out.println("RealStar.confer()"); } @Override public void signContract() { System.out.println("RealStar.signContract()"); } @Override public void bookTicket() { System.out.println("RealStar.bookTicket()"); } @Override public void sing() { System.out.println("RealStar(周杰伦).sing() "); } @Override public void collectMoney() { System.out.println("RealStar.collectMoney()"); } }
public class ProxyStar implements Star { private Star star; public ProxyStar(Star star) { this.star = star; } @Override public void confer() { System.out.println("ProxyStar.confer()"); } @Override public void signContract() { System.out.println("ProxyStar.signContract()"); } @Override public void bookTicket() { System.out.println("ProxyStar.bookTicket()"); } @Override public void sing() { star.sing(); } @Override public void collectMoney() { System.out.println("ProxyStar.collectMoney()"); } }
public class Client { public static void main(String[] args) { Star realStar = new RealStar(); Star proxy = new ProxyStar(realStar); proxy.confer(); proxy.signContract(); proxy.bookTicket(); proxy.sing(); proxy.collectMoney(); } }
在创建多线程中也应用了静态代理
public class Programmer implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("敲代码"); } } }
public class ProgrammerApp { public static void main(String[] args) { //创建真实角色 Programmer pro = new Programmer(); //创建代理角色+真实角色引用 Thread th = new Thread(pro); th.start(); for (int i = 0; i < 10; i++) { System.out.println("吃水果"); } } }
2)动态代理(动态生成代理类)
优点:抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵魂和统一的处理众多的方法。
1>JDK自带的动态代理
-java.lang.reflect.Proxy 作用:动态生成代理类和对象
-java.lang.reflect.InvocationHandler(处理器的接口)
可以通过invoke方法实现对真实角色的代理访问
每次通过Proxy生成代理类对象时都要指定对应的处理器对象
public class Client { public static void main(String[] args) { //真实角色 Star realStar = new RealStar(); //处理器接口 StarHandler handler = new StarHandler(realStar); //动态生成代理类和对象 Star proxy = (Star) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, handler); //proxy.bookTicket(); proxy.sing(); } }
public class StarHandler implements InvocationHandler { Star realStar; public StarHandler(Star realStar) { super(); this.realStar = realStar; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("真正的方法执行前"); System.out.println("面谈、签合同、预付款、订票"); if (method.getName().equals("sing")) { method.invoke(realStar, args); } System.out.println("真正方法执行后"); System.out.println("收尾款"); return null; } }
2>javassist字节码操作库实现
提供源码级别和字节码级别的API。AOP(面向切面编程):在某条语句前面、后面 或者 前后 加代码。
主要由CtClass、CtMethod、以及CtFiled几个类组成 (Ct代表Compile time 编译时生成类、方法、属性等等)
通过javassist类库生成一个类
/* * Ct ---> Compile time 编译时 * */ public class Demo { public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException { ClassPool pool = ClassPool.getDefault(); //类池 //创建一个类 CtClass cc = pool.makeClass("cn.hyy.bean.Emp"); //创建属性 CtField f1 = CtField.make("private int empno;", cc); CtField f2 = CtField.make("private String ename;", cc); cc.addField(f1); cc.addField(f2); //创建方法 CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc); CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno = empno;}", cc); cc.addMethod(m1); cc.addMethod(m2); //添加构造器 CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, cc);//传参 ctConstructor.setBody("{this.empno =empno;this.ename =ename;}"); cc.addConstructor(ctConstructor); cc.writeFile("/home/deepin/IdeaProject/myjava"); System.out.println("生成类,成功"); } }
@Author(name = "Chiba", year = 2005) public class Emp { private int empno; private String ename; public int getEmpno() { return empno; } public void setEmpno(int empno) { this.empno = empno; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public Emp() { } public Emp(int empno, String ename) { this.empno = empno; this.ename = ename; } public void sayHello(int a) { System.out.println("sayHello," + a); } }
public @interface Author { String name(); int year(); }
javassist API详解
public class Demo02 { /* * 处理类的基本用法 * */ public static void test01() throws Exception { ClassPool pool = ClassPool.getDefault(); //获得某个类(已有的类)的CtClass CtClass cc = pool.get("cn.hyy.test.testJavassist.Emp"); //调用了writeFile(),toClass(),toBytecode()会出现冰冻现象 //其他的方法不能修改此类,若要恢复,可调用defrost()方法进行解冻 byte[] bytes = cc.toBytecode(); //获得字节码 //cc.defrost(); //可以解冻 System.out.println(Arrays.toString(bytes)); System.out.println(cc.getName()); //获取类名 System.out.println(cc.getSimpleName()); //获取简单类名 System.out.println(cc.getSuperclass()); //获得父类 System.out.println(cc.getInterfaces()); //获得接口 } /* * 测试产生新的方法 * */ public static void test02() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.hyy.test.testJavassist.Emp"); //CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc); CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, cc); //声明方法 m.setModifiers(Modifier.PUBLIC); //设置访问权限 m.setBody("{System.out.println(\"www.sxt.cn\");return $1+$2;}"); //$1第一个形参,$2第二个形参 cc.addMethod(m); //通过反射调用新生成的方法 Class clazz = cc.toClass(); //获得Class对象 //cc.defrost(); //可以解冻 Object obj = clazz.newInstance(); //通过调用Emp无参构造器,创建新的Emp对象 Method method = clazz.getDeclaredMethod("add", int.class, int.class); Object result = method.invoke(obj, 200, 300); System.out.println(result); } //修改已有方法的信息,修改方法体的内容 public static void test03() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.hyy.test.testJavassist.Emp"); CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType}); //加到方法体的前面 cm.insertBefore("System.out.println($1);System.out.println(\"start!!\");"); //加到方法体的后面 cm.insertAfter("System.out.println(\"end!!!\");"); //加到方法体的指定位置(在某一行前面加代码) cm.insertAt(37, "int b=3; System.out.println(\"b=\"+b);"); //通过反射调用新生成的方法 Class clazz = cc.toClass(); Object obj = clazz.newInstance(); //通过调用Emp无参构造器,创建新的Emp对象 Method method = clazz.getDeclaredMethod("sayHello", int.class); method.invoke(obj, 300); } //属性的操作--新增属性 public static void test04() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.hyy.test.testJavassist.Emp"); //CtField f1 = CtField.make("private int empno;"cc); CtField f1 = new CtField(CtClass.intType, "salary", cc); f1.setModifiers(Modifier.PRIVATE); cc.addField(f1,"1000"); //可以设置默认值 //cc.getDeclaredField("salary");//获取指定的属性 //增加相应的set和get方法 cc.addMethod(CtNewMethod.getter("getSalary", f1)); cc.addMethod(CtNewMethod.setter("setSalary", f1)); } //构造器的操作 public static void test05() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("cn.hyy.test.testJavassist.Emp"); //获得所有的构造器 CtConstructor[] cs = cc.getConstructors(); for (CtConstructor c : cs) { //还可以在构造器方法体前面、后面加代码 System.out.println(c.getLongName()); } } //注解的操作 public static void test06() throws Exception { CtClass cc = ClassPool.getDefault().get("cn.hyy.test.testJavassist.Emp"); Object[] all = cc.getAnnotations(); //获得此类所有的注解,这里只有一个注解 Author a = (Author) all[0]; String name = a.name(); int year = a.year(); System.out.println("name:" + name + ",year:" + year); } public static void main(String[] args) throws Exception { //test01(); //test02(); //test03(); //test04(); //test05(); test06(); } }
3>CGLIB(字节码生成库)
4>ASM(底层使用指令,可维护性较差)
3.桥接模式
未使用桥接模式处理这个问题
public interface Computer { void sale(); } class Desktop implements Computer { @Override public void sale() { System.out.println("销售台式机"); } } class Laptop implements Computer { @Override public void sale() { System.out.println("销售笔记本"); } } class Pad implements Computer { @Override public void sale() { System.out.println("销售平板电脑"); } } class LenovoDesktop extends Desktop { @Override public void sale() { System.out.println("销售联想台式机"); } } class LenovoLaptop extends Laptop { @Override public void sale() { System.out.println("销售联想笔记本"); } } class LenovoPad extends Pad { @Override public void sale() { System.out.println("销售联想平板电脑"); } } class ShenZhouDesktop extends Desktop { @Override public void sale() { System.out.println("销售神舟台式机"); } } class ShenZhouLaptop extends Laptop { @Override public void sale() { System.out.println("销售神舟笔记本"); } } class ShenZhouPad extends Pad { @Override public void sale() { System.out.println("销售神舟平板电脑"); } } class DellDesktop extends Desktop { @Override public void sale() { System.out.println("销售戴尔台式机"); } } class DellLaptop extends Laptop { @Override public void sale() { System.out.println("销售戴尔笔记本"); } } class DellPad extends Pad { @Override public void sale() { System.out.println("销售戴尔平板电脑"); } }
使用桥接模式是两个维度的类关联起来
桥接模式核心要点:处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承类,使各个维度可以独立的扩展在抽象层建立关联。
/* * 电脑类型维度 * */ public class Computer2 { protected Brand brand; public Computer2(Brand brand) { this.brand = brand; } public void sale() { brand.sale(); } } class Desktop2 extends Computer2 { public Desktop2(Brand brand) { super(brand); } @Override public void sale() { super.sale(); System.out.println("销售台式机"); } } class Laptop2 extends Computer2 { public Laptop2(Brand brand) { super(brand); } @Override public void sale() { super.sale(); System.out.println("销售笔记本"); } }
/* *电脑品牌维度 * */ public interface Brand { void sale(); } class Lenovo implements Brand { @Override public void sale() { System.out.println("销售联想电脑"); } } class Dell implements Brand { @Override public void sale() { System.out.println("销售戴尔电脑"); } } class ShenZhou implements Brand { @Override public void sale() { System.out.println("销售神舟电脑"); } }
public class Client { public static void main(String[] args) { //销售联想的笔记本电脑 //通过组合实现继承关系 Computer2 c = new Laptop2(new Lenovo()); c.sale(); //销售神舟台式机 Computer2 c2 = new Desktop2(new ShenZhou()); c2.sale(); } }
4.组合模式
使用组合模式的场景:
--把部分和整体的关系用树形结构来表示,从而是客户端可以用统一的方式处理部分对象和整体对象
组合模式核心:
1)抽象构件(Component)角色:定义了叶子和容器构件的共同点
2)叶子(Leaf)构件角色:无子节点
3)容器(Composite)构件角色:有容器特征,可以包含子节点
组合模式工作流程分析:
--组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子节点进行递归组合,使得用户在使用时可以一致性的对待容器和叶子。
--当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。其中,使用了递归调用机制对整个结构进行处理。
/* * 抽象构件 * */ public interface AbstractFile { void killVirus(); //杀毒 } class ImageFile implements AbstractFile { private String name; public ImageFile(String name) { this.name = name; } @Override public void killVirus() { System.out.println("--图像文件--" + name+"进行查杀"); } } class TextFile implements AbstractFile { private String name; public TextFile(String name) { this.name = name; } @Override public void killVirus() { System.out.println("--文本文件--" + name+"进行查杀"); } } class VideoFile implements AbstractFile { private String name; public VideoFile(String name) { this.name = name; } @Override public void killVirus() { System.out.println("--视频文件--" + name+"进行查杀"); } } class Folder implements AbstractFile { private String name; //定义容器,用来存放容器构件下的子节点 private List<AbstractFile> list = new ArrayList<>(); public Folder(String name) { this.name = name; } public void add(AbstractFile file) { list.add(file); } public void remove(AbstractFile file) { list.remove(file); } public AbstractFile getChild(int index) { return list.get(index); } @Override public void killVirus() { System.out.println("--文件夹--" + name + "进行查杀"); for (AbstractFile file : list) { file.killVirus(); //子文件的查杀 } } }
public class Client { public static void main(String[] args) { AbstractFile f1, f2, f3, f4, f5, f6; AbstractFile f11; f1 = new Folder("我的收藏"); f2 = new ImageFile("我的图像.jpg"); f3 = new TextFile("hello.txt"); ((Folder) f1).add(f2); ((Folder) f1).add(f3); f11 = new Folder("电影"); f4 = new VideoFile("笑傲江湖.avi"); f5 = new VideoFile("神雕侠侣.avi"); ((Folder) f11).add(f4); ((Folder) f11).add(f5); ((Folder) f1).add(f11); //f2.killVirus(); f1.killVirus(); } }
5.装饰模式(decorator)
职责:
1)动态的为一个对象增加新的功能。
2)装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
实现细节:
--Component抽象构件角色
真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。
--ConcreateComponent具体构件角色(真实对象)
io流中的FileInputaStream、FileOutputStream
--Decorator装饰角色
持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求发给真实对象。这样,就能在真实对象调用前后增加新的功能。
--ConcreateDecorator 具体装饰角色
负责给构件对象增加新的功能
/* * 抽象构件角色 * */ public interface ICar { void move(); } //具体构件角色(真实对象) class Car implements ICar { @Override public void move() { System.out.println("陆地上跑"); } } //装饰角色 class SuperCar implements ICar { protected ICar car; public SuperCar(ICar car) { this.car = car; } @Override public void move() { car.move(); } } //具体装饰对象 class FlyCar extends SuperCar { public FlyCar(ICar car) { super(car); } public void fly() { System.out.println("天上飞"); } @Override public void move() { super.move(); fly(); } } class WaterCar extends SuperCar { public WaterCar(ICar car) { super(car); } public void swim() { System.out.println("水上游"); } @Override public void move() { super.move(); swim(); } } class AICar extends SuperCar { public AICar(ICar car) { super(car); } public void autoMove() { System.out.println("自动跑"); } @Override public void move() { super.move(); autoMove(); } }
public class Client { public static void main(String[] args) { Car car = new Car(); car.move(); System.out.println("增加新的功能,飞行----"); FlyCar flyCar = new FlyCar(car); flyCar.move(); System.out.println("增加新的功能,水里游----"); WaterCar waterCar = new WaterCar(car); waterCar.move(); System.out.println("增加两个新的功能,飞行,水里游----"); WaterCar waterCar1 = new WaterCar(new FlyCar(car)); waterCar1.move(); } }
6.外观模式
遵从迪米特法则(最少知识法则):只与你直接的朋友通信,而避免和陌生人通信。
--为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
以注册公司流程为例
1)不使用外观模式
public interface 工商局 { void checkName();//核名 } class 海淀区工商区 implements 工商局 { @Override public void checkName() { System.out.println("检查名字是否有冲突"); } }
public interface 税务局 { void taxCertificate();//办理税务登记证 } class 海淀区税务局 implements 税务局 { @Override public void taxCertificate() { System.out.println("在海淀区税务局办理税务登记证!"); } }
public interface 质检局 { void orgCodeCertificate(); //办理组织机构代码证 } class 海淀区质检局 implements 质检局 { @Override public void orgCodeCertificate() { System.out.println("在海淀区质检局办理组织机构代码证"); } }
public interface 银行 { void openAccount();//开户 } class 中国工商银行 implements 银行 { @Override public void openAccount() { System.out.println("在工商银行开户"); } }
public class Client { public static void main(String[] args) { 工商局 a = new 海淀区工商区(); a.checkName(); 质检局 b = new 海淀区质检局(); b.orgCodeCertificate(); 税务局 c = new 海淀区税务局(); c.taxCertificate(); 银行 d = new 中国工商银行(); d.openAccount(); } }
2)使用外观模式
/* * 办理注册公司流程的门面对象 * */ public class RegisterFacade { public void register() { 工商局 a = new 海淀区工商区(); a.checkName(); 质检局 b = new 海淀区质检局(); b.orgCodeCertificate(); 税务局 c = new 海淀区税务局(); c.taxCertificate(); 银行 d = new 中国工商银行(); d.openAccount(); } }
7.享元模式(FlyWeight)
场景:内存属于稀缺资源,不要随便浪费。如果有很多完全相同或相似的对象,我们可以通过享元模式,节省内存。
核心:
1)享元模式以共享的方式高效地支持大量细粒度对象的重用
2)享元模式能做到共享的关键是区分了内部状态和外部状态
内部状态:可以共享,不会随环境变化而变化
外部状态:不可以共享,会随环境变化而变化
享元模式的实现:
--FlyweightFactory享元工厂类
创建并管理享元对象,享元池一般设计成键值对
--FlyWeight抽象享元类
通常是一个接口或者抽象类,声明公开方法,这些方法可以向外界提供对象的内部状态,设置外部状态
--ConcreateFlyweight具体享元类
为内部状态提供成员变量进行存储
--UnsharedConcreateFlyweight非共享享元类
不能被共享的子类可以设计为非共享享元类
/* * 抽象享元类,声明公开方法,这些方法可以向外界提供对象的内部状态,设置外部状态 * */ public interface ChessFlyWeight { void setColor(String color); String getColor(); void display(Coordinate c); } /*具体享元类ConcreateFlyweigh * */ class ConcreateChess implements ChessFlyWeight { private String color; //为内部状态提供成员变量进行存储 public ConcreateChess(String color) { this.color = color; } @Override public void setColor(String color) { this.color = color; } @Override public String getColor() { return color; } @Override public void display(Coordinate c) { System.out.println("棋子颜色" + color); System.out.println("棋子位置"+c.getX()+"---"+c.getY()); } }
/*外部状态UnsharedConcreateFlyweight * */ public class Coordinate { private int x,y; public Coordinate(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
/* * 抽象享元类,声明公开方法,这些方法可以向外界提供对象的内部状态,设置外部状态 * */ public interface ChessFlyWeight { void setColor(String color); String getColor(); void display(Coordinate c); } /*具体享元类ConcreateFlyweigh * */ class ConcreateChess implements ChessFlyWeight { private String color; //为内部状态提供成员变量进行存储 public ConcreateChess(String color) { this.color = color; } @Override public void setColor(String color) { this.color = color; } @Override public String getColor() { return color; } @Override public void display(Coordinate c) { System.out.println("棋子颜色" + color); System.out.println("棋子位置"+c.getX()+"---"+c.getY()); } }
/* * 享元工厂类,创建并管理享元对象,享元池一般设计成键值对 * */ public class ChessFlyWeightFactory { //享元池对象 private static Map<String, ChessFlyWeight> map = new HashMap<>(); public static ChessFlyWeight getChess(String color) { if (map.get(color) != null) { return map.get(color); } else { ChessFlyWeight cfw = new ConcreateChess(color); map.put(color, cfw); return cfw; } } }
public class Client { public static void main(String[] args) { ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色"); ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色"); System.out.println(chess1); System.out.println(chess2); System.out.println("增加外部状态的处理"); chess1.display(new Coordinate(10, 10)); chess2.display(new Coordinate(20,20)); } }