23种设计模式——结构型模型
结构型模型的核心作用是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
public interface Target { void handleReq(); }
然后准备被适配的类
/** * 被适配的类 */ public class Adaptee { public void request() { System.out.println("可以完成客户请求的需要功能!"); } }
然后就是适配器
适配器适配被适配的类有两种方式:
- 一种是直接继承被适配的类,该方式的缺点是java只能单继承,无法完成适配多个类
- 第二种是使用对象组合的方式
方式一
/** * 适配器本身 * 需要把被适配对象Adaptee跟目标对象Target连接起来 */ public class Adapter extends Adaptee implements Target { @Override public void handleReq() { super.request(); } }
方式二
/** * 适配器本身 * 需要把被适配对象Adaptee跟目标对象Target连接起来 */ public class Adapter2 implements Target { private Adaptee adaptee; public Adapter2(Adaptee adaptee) { super(); this.adaptee = adaptee; } @Override public void handleReq() { adaptee.request(); } }
使用
Target t = new Adapter(); t.handleReq();
-
-
java.io.InputStreamReader(InputStream)
-
java.io.OutputStreamWriter(OutputStream)
作用:通过代理,控制对对象的访问。在调用某个方法前做前置处理,调用这个方法后做后置处理
-
抽象角色:定义代理角色和真实角色的公共对外方法
-
真实角色:实现抽象角色,定义真实角色要实现的业务逻辑
-
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并且可以附加自己的操作
分类:
-
静态代理(静态定义代理类)
-
动态代理(动态生成代理类)
public interface Star { /** * 面谈 */ void confer(); /** * 签合同 */ void signContract(); /** * 订票 */ void bookTicket(); /** * 唱歌 */ void sing(); /** * 收钱 */ void collectMoney(); }
真实角色
public class RealStar implements Star { @Override public void bookTicket() { System.out.println("bookTicket"); } @Override public void collectMoney() { System.out.println("collectMoney"); } @Override public void confer() { System.out.println("confer"); } @Override public void signContract() { System.out.println("signContract"); } @Override public void sing() { System.out.println("sing"); } }
代理角色
public class ProxyStar implements Star { private Star star; // 被代理的对象 public ProxyStar(Star star) { this.star = star; } @Override public void bookTicket() { System.out.println("bookTicket"); } @Override public void collectMoney() { System.out.println("collectMoney"); } @Override public void confer() { System.out.println("confer"); } @Override public void signContract() { System.out.println("signContract"); } @Override public void sing() { // 调用真实角色的方法 star.sing(); } }
使用
Star star = new ProxyStar(new RealStar()); star.confer(); star.signContract(); star.bookTicket(); star.sing(); star.collectMoney();
动态代理的实现方式
-
JDK自带的动态代理
-
javaassist字节码操作库实现
-
CGLIB
-
ASM(底层使用指令,可维护性较差)
使用JDK自带的动态代理
-
-
该类的作用是动态的生成代理类对象
-
-
java.lang.reflect.InvocationHandler(处理器接口)
-
可以通过invoke方法实现对真实角色的代理访问,所有的流程控制都在这个方法中处理
-
每次通过Proxy生成代理类对象时都要指定对应的处理器对象
-
使用动态代理还是需要实现抽象角色
public interface Star { /** * 面谈 */ void confer(); /** * 签合同 */ void signContract(); /** * 订票 */ void bookTicket(); /** * 唱歌 */ void sing(); /** * 收钱 */ void collectMoney(); }
真实角色 这是必须的
public class RealStar implements Star { @Override public void bookTicket() { System.out.println("bookTicket"); } @Override public void collectMoney() { System.out.println("collectMoney"); } @Override public void confer() { System.out.println("confer"); } @Override public void signContract() { System.out.println("signContract"); } @Override public void sing() { System.out.println("sing"); } }
代理角色
这一块换成了动态代理,需要自定义handler在执行方法前后执行某些操作
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class StarHandler implements InvocationHandler { private Star realStar; public StarHandler(Star star) { this.realStar = star; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object obj = null; if (method.getName().equals("sing")) { System.out.println("执行前"); } // 执行当前方法 obj = method.invoke(realStar, args); System.out.println("执行后"); return obj; } }
使用
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 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("戴尔电脑"); } }
然后对电脑类型进行定义
/** * 电脑类型 */ public class Computer { protected Brand brand; public Computer(Brand brand) { this.brand = brand; } public void sale() { brand.sale(); } } class Desktop extends Computer { public Desktop(Brand b) { super(b); } @Override public void sale() { super.sale(); System.out.println("销售台式机"); } } class Laptop extends Computer { public Laptop(Brand b) { super(b); } @Override public void sale() { super.sale(); System.out.println("销售笔记本"); } }
使用
现在想要一个品牌的电脑就不需要定义一个类了,直接组合即可
public static void main(String[] args) { Computer c = new Laptop(new Lenovo()); c.sale(); }
组合模式用于处理属性结构,不论是处理叶子节点还是容器节点都有一个统一的处理方式
核心组成:
-
抽象构建角色,定义了叶子和容器构建共同遵循的规则,也是供客户端调用的接口
-
叶子构建角色,无子节点
-
容器构建角色,有容器特征,可以包含子节点
/** * 抽象组件 */ public interface Component { void operation(); } /** * 叶子组件 */ interface Leaf extends Component { } /** * 容器组件 */ interface Composite extends Component { void add(Component c); // 添加组件 void remove(Component c); // 删除组件 Component getChild(int index); // 返回指定的组件 }
import java.util.ArrayList; import java.util.List; /** * 抽象构建 */ 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 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 + "进行查杀"); list.forEach(AbstractFile::killVirus); } }
使用
public static void main(String[] args) { Folder f1 = new Folder("我的收藏"); AbstractFile f2, f3; f2 = new ImageFile("1.jpg"); f3 = new TextFile("HHH.txt"); f1.add(f2); f1.add(f3); f1.killVirus(); }
-
动态的为一个对象增加新的功能
-
装饰模式是一种用于替代继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系替代继承关系,更加灵活,同时避免类型体系的快速膨胀
实现细节
-
Component抽象构建角色:真实对象和装饰对象有相同的接口,这样客户端对象就能以与真实对象相同的方式同装饰对象交互
-
ConcreateComponent具体构建角色(真实对象)
-
Decorate装饰角色,持有一个抽象构建的引用,装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象,这样,就能在真实对象调用前后增加新的功能
-
ConcreateDecorate具体装饰角色
/** * 抽象组件 */ public interface ICar { void move(); } // 真实对象,即被装饰对象 class Car implements ICar { @Override public void move() { System.out.println("陆地上跑!"); } } /** * Decorate装饰器角色 */ class SuperCar implements ICar { protected ICar iCar; public SuperCar(ICar iCar) { this.iCar = iCar; } @Override public void move() { iCar.move(); } } /** * ConcreateDecorate具体装饰角色 */ class FlyCar extends SuperCar { public FlyCar(ICar iCar) { super(iCar); } public void fly() { System.out.println("天上飞"); } @Override public void move() { fly(); super.move(); } } /** * ConcreateDecorate具体装饰角色 */ class WaterCar extends SuperCar { public WaterCar(ICar iCar) { super(iCar); } public void swim() { System.out.println("水上游"); } @Override public void move() { swim(); super.move(); } }
使用
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(flyCar); waterCar.move(); }
迪米特法则(最少知识原则):一个软件实体应当尽可能少的与其它实体发生相互作用
核心:为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
作用:内存属于稀缺资源,不能随意浪费。如果有很多个完全相同或相似的对象,我们可以使用享元模式,节省内存
享元模式的关键是做到了区分内部状态跟外部状态
-
内部状态:可以共享,不会随环境的变化而改变
-
外部状态:不可以共享,会随环境的变化而变化
-
-
Flyweight抽象享元类:通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态
-
ConcreateFlyWeight具体享元类:为内部状态提供成员变量进行存储
-
UnsharedConcreateFlyWeight非共享享元类:不能被共享的子类可以设计为非共享享元类
FlyweightFactory享元工厂
import java.util.HashMap; import java.util.Map; /** * 享元工厂类 */ 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; } } }
Flyweight抽象享元类&ConcreateFlyWeight具体享元类
/** * 享元类 */ public interface ChessFlyWeight { void setColor(String color); String getColor(); void display(Coordinate c); } 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非共享享元类
/** * 外部状态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 static void main(String[] args) { ChessFlyWeight c1 = ChessFlyWeightFactory.getChess("黑色"); ChessFlyWeight c2 = ChessFlyWeightFactory.getChess("黑色"); System.out.println(c1); System.out.println(c2); System.out.println("增加外部状态的处理=============="); c1.display(new Coordinate(10, 10)); c2.display(new Coordinate(20, 20)); }
-
-
为了节省内存,共享了内存状态,分离出外部状态,而读取外部状态,使运行时间变长。用时间换取了空间