核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
适配器设计模式:
将一个类的接口转换成客户希望的另一个的接口
适配器使原本由于接口不兼容而不能工作的那些类可以一起工作
适配器比较经典的案例
java.io.InputStreamReader(InputStream) java.io.OutputStreamWriter(OutputStream)
感觉是配置有一点类似静态代理,但是适配器可能需要继承或者实现某一个类或者接口
采用组合的方式(推荐使用)
public class Demo { public static void main(String[] args){ work(new PowerAachieve()); //work(new PowerBachieve()); //接口A接受不了接口B Adapter adapter = new Adapter(new PowerBachieve()); //利用适配器接入接口A。接口B接入到适配器上即可 work(adapter); } public static void work(PowerA power){ //接口A(可以接受是适配器,不能接受接口B) power.insert(); } } interface PowerA{ void insert(); } class PowerAachieve implements PowerA{ public void insert(){ System.out.println("接口A开始工作"); } } //适配器 class Adapter implements PowerA{ //适配器必须继承接口B,将接口B接入到适配器上 private PowerB power; public Adapter(PowerB power){ this.power = power; } public void insert(){ this.power.connect(); } } interface PowerB{ void connect(); } class PowerBachieve implements PowerB{ public void connect(){ System.out.println("接口B开始工作"); } }
采用继承的方式(适配器继承需要调用的类,不推荐使用,作为了解)
public class Demo { public static void main(String[] args){ Adapter adapter = new Adapter(); //利用适配器接入接口A。接口B接入到适配器上即可 work(adapter); } public static void work(PowerA power){ //接口A(可以接受是适配器,不能接受接口B) power.insert(); } } interface PowerA{ void insert(); } class PowerAachieve implements PowerA{ public void insert(){ System.out.println("接口A开始工作"); } } class Adapter extends PowerBachieve implements PowerA{ //适配器必须继承接口B,将接口B接入到适配器上 public void insert(){ this.connect(); } } interface PowerB{ void connect(); } class PowerBachieve implements PowerB{ public void connect(){ System.out.println("接口B开始工作"); } }
适配器用途二
public class Demo { public static void main(String[] args){ work(new Dog()); } public static void work(Animal animal){ //接口Animal(可以接受是适配器) animal.run(); } } interface Animal{ void run(); void sing(); void swim(); void fly(); } //class Dog implements Animal{ // public void run(){}; // public void sing(){}; // public void swim(){ // System.out.println("如果只要实现swim方法,但是需要将所有继承的方法都是实现(可以不写其代码)"); // }; // public void fly(){}; //} //利用适配器实现所有的接口,其他的类直接继承适配器.这样的好处是不用每一个类都要实现所有的方法. abstract class Adapter implements Animal{ public void run(){}; public void sing(){}; public void swim(){}; public void fly(){}; } class Dog extends Adapter{ public void run(){ System.out.println("我可以跑"); } }
静态代理模式:
注意代理的核心思想就是做AOP
在运行前就已经将代理写好了
为其他对象提供一种代理以控制对这个对象的访问
被代理的代码有完整的业务实现,可以单独运行。而通过代理可以添加一些其他的需求。然后通过代理来控制被代理的业务实现。
静态代理类对象是由自己实现的:缺陷,如果被代理的对象需要增加方法,那么代理类也需要额外添加对应的方法,而且会造成代码冗余。
public class Demo { public static void main(String[] args){ Proxy proxy = new Proxy(new Phone()); proxy.work(); } } interface Action{ void work(); } class Phone implements Action{ //被代理 public void work(){ System.out.println("手机开始工作"); } } //代理类必须实现被代理类的所有的方法,所以需要实现被代理类的所有的方法; class Proxy implements Action{ private Action action; public Proxy(){} public Proxy(Action action){ this.action = action; } public void work(){ long starttime = System.currentTimeMillis(); this.action.work(); long endtime = System.currentTimeMillis(); System.out.println("一共耗时:"+(endtime - starttime)+"毫秒"); } }
动态代理模式:
这样创建出来的代理类是什么样的?
大概是程序会自动生成一个代理类,这给代理类实现了所有的接口(我们传入的),代理类对象就会实现接口中的所有的方法,方法里面的逻辑都是去调用invoke()方法;
动态代理,就是根据对象在内存中加载的Class类创建运行时类对象,从而调用代理类方法和属性。
public class Demo { public static void main(String[] args) { CreateProxy createproxy = new CreateProxy(); Customer customer = new Customer(); //注意只能强转成接口类型 Subject Subject subject = (Subject) createproxy.create(customer); subject.shopping(); //会调用invoke方法 } } interface Subject { void shopping(); } class Customer implements Subject { @Override public void shopping() { System.out.println("付款,购买物品"); } } //用于动态生成一个代理对象 class CreateProxy implements InvocationHandler { //传入target的目的是因为invoke(target, args)方法需要传入一个target对象,这个对象就是被代理的对象 private Object target; public Object create(Object target) { this.target = target; //newProxyInstance参数类加载器,接口数组,this,接口是动态加载的(可以调用多个接口) //ClassLoader loader:类加载器 //Class<?>[] interfaces:接口数组,可以写成这样:new Class[]{Subject.class} //InvocationHandler h: 实现InvocationHandler接口的类 Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return proxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()); //可以用它来判断特定的方法执行不同的功能; System.out.println("正在挑选商品....."); System.out.println("已挑选好商品.....,等待客户确认订单"); method.invoke(target, args); System.out.println("订单完成"); return null; } }
静态代理和动态代理的对比
参考:https://www.cnblogs.com/baizhanshi/p/6611164.html 写的很详细
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)
缺点:1、代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2、代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态:在程序运行时运用反射机制动态创建而成。
优点:如果静态代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类,而动态代理,只需要一个代理类,在代理类中添加不同的代理方法(返回不同的代理对象)即可。
不需要实现委代理类的每一个实现方法,减少代码的冗余
桥接模式:
采用组合方式解决多继承的复杂模式
桥接模式核心要点:
-处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
使用场景
--JDBC驱动程序
--AWT中的Peer架构
--银行日志管理:
·格式分类:操作日志、交易日志、异常日志
·距离分类:本地记录日志、异地记录日志
--人力资源系统中的奖金计算模块:
奖金分类:个人奖金、团体奖金、激励奖金。·
部门分类:人事部门、销售部门、研发部门。
--OA系统中的消息处理:
业务类型:普通消息、加急消息、特急消息
发送消息方式:系统内消息、手机短信、邮件
需求:有一个品牌电脑A和B,他们下面都要有2个销售的类型(笔记本和台式机),所以笔记本类和台式机类搜需要继承各自的品牌类
如果现在又要添加一个新的品牌,就又要添加2个销售的类型(这会造成代码冗余,笔记本类的代码都是一样的);
我们可以将品牌和笔记本/台式机类采用组合的方式,桥接起来
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("销售Dell电脑"); } }
public class Computer { private Brand brand; public Computer(Brand brand) { this.brand = brand; } public void sale(){ this.brand.sale(); } } class Desktop extends Computer{ public Desktop(Brand brand) { super(brand); } @Override public void sale() { super.sale(); System.out.println("销售台式机"); } } class laptop extends Computer{ public laptop(Brand brand) { super(brand); } @Override public void sale() { super.sale(); System.out.println("销售笔记本"); } }
demo
public class Demo { public static void main(String[] args) { Desktop desktop = new Desktop(new Lenovo()); desktop.sale(); } }
组合模式:
组合和组合设计模式不一样
组合在A类里边添加一个B类这个属性(可以通过构造方法传入B类对象),此时A类就可以调用B类的所有的方法了
使用场景
把部分和整体的关系用树形结构(类似二叉树)来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。
组合模式核心:
-抽象构件(Component)角色:定义了叶子和容器构件的共同点
-叶子(Leaf)构件角色:无子节点
-容器(Composite)构件角色:有容器特征,可以包含子节点
开发中的应用场景:
-操作系统的资源管理器-GUI中的容器层次图
-XML文件解析
-OA系统中,组织结构的处理
-Junit单元测试框架
底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
抽象组件
public interface Component { void operation(); } //容器节点 interface Composite extends Component{ void add(Component c); void remove(Component c); Component getChild (int index); } //叶子节点 interface leaf extends Composite{ }
使用组合模式,模拟杀毒软件架构设计
public interface AbstractFile { void killVirus(); } //叶子节点 class ImageFile implements AbstractFile{ @Override public void killVirus() { System.out.println("ImageFile正在被查杀"); } } //叶子节点 class TextFile implements AbstractFile{ @Override public void killVirus() { System.out.println("TextFile正在被查杀"); } } //容器节点 class Folder implements AbstractFile{ private List<AbstractFile> list = new ArrayList<AbstractFile>(); //通过容器节点将各个节点关联起来 @Override public void killVirus() { System.out.println("Folder进行查杀"); list.forEach((AbstractFile abstractFile)->{abstractFile.killVirus();}); } void add(AbstractFile c){ list.add(c); }; void remove(AbstractFile c){ list.remove(c); }; AbstractFile getChild (int index){ AbstractFile abstractFile = list.get(index); return abstractFile; }; }
demo
public class Demo { public static void main(String[] args) { Folder folder = new Folder(); folder.add(new TextFile()); folder.add(new ImageFile()); folder.add(new TextFile()); //操作容器 folder.killVirus(); //操作叶子 new TextFile().killVirus(); } }
装饰者设计模式:
动态为对象添加新的功能(可以修改对象的属性,可以进行方法的扩展)
开发中使用的场景:
-IO中输入流和输出流的设计
-Swing包中图形界面构件功能
-Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。
-Struts2中,request,response,session对象的处理
public class Test { public static void main(String[] args){ Milk milk = new Milk(); Coffer coffer = new Coffer(milk); Coke coke = new Coke(coffer); System.out.println(coke.cost()+","+coke.desc()); } } interface Drink{ float cost(); String desc(); } class Milk implements Drink{ @Override public float cost() { return 10f; } @Override public String desc() { return "牛奶"; } } //如果没有这个所有的修饰类都需要添加Drink属性,代码冗余 abstract class Decorator implements Drink{ private Drink drink; public Decorator(Drink drink){ this.drink = drink; } @Override public float cost() { return this.drink.cost(); } @Override public String desc() { return this.drink.desc(); } } class Coffer extends Decorator{ public Coffer(Drink drink) { super(drink); } @Override public float cost() { return super.cost()+1.0f; } @Override public String desc() { return super.desc()+"加咖啡"; } } class Coke extends Decorator{ public Coke(Drink drink) { super(drink); } @Override public float cost() { return super.cost()+1.0f; } @Override public String desc() { return super.desc()+"加可乐"; } }
外观模式:
迪米特法则(最少知识原则)
一个软件实体应当尽可能少的与其他实体发生相互作用。
外观模式核心
为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
class A{ void test(){ System.out.println("去工商局"); } } class B{ void test(){ System.out.println("去税务局"); } } public class Door { //注册公司 void register(){ new A().test(); new B().test(); } }
demo
public class Demo { public static void main(String[] args) { //new A().test(); //new B().test(); //只需要和这一个类打交道 new Door().register(); } }
享元模式:
场景:
内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存,但是增加了运行时间;
核心:
享元模式以共享的方式高效地支持大量细粒度对象的重用。
享元对象能做到共享的关键是区分了内部状态和外部状态。
内部状态:可以共享,不会随环境变化而改变
外部状态:不可以共享,会随环境变化而改变
比如一个围棋中,所有的棋子大小形状颜色都是可以共享的,但是棋子的位置不能共享
享元模式实现
普通类:包含了所有的共享变量,有一个方法可以传入非共享变量(该变量不能被存储,所以方法结束变量清空,所以需要在方法中将非共享变量作用使用出来);
享元工厂:使用单例缓冲值,同一个key的对象只能有一个(如果全部对象都只有一个key,完全可以用单例来代替享元工厂)
开发过程中应用场景
享元模式由于其共享的特性,可以在任何“池"中操作,比如:线程池、数据库连接池。
String类的设计也是享元模式
//享元类 public interface ChessFlyWeight { void setColor(String c); String getColor(); void display(Coordinate coordinate); } class Coordinate{ public int x; public int y; public Coordinate(int x, int y) { this.x = x; this.y = y; } } class ConcreteChess implements ChessFlyWeight{ public String color; public ConcreteChess(String color) { this.color = color; } @Override public void setColor(String c) { color =c; } @Override public String getColor() { return color; } @Override public void display(Coordinate coordinate) { System.out.println("棋子颜色:"+color); System.out.println("棋子位置:x="+coordinate.x+",y="+coordinate.y); } } //享元工厂类(核心使用了单例缓冲池) class ChessFlyWeightFactory{ //享元池 private static Map<String,ChessFlyWeight> map= new HashMap<>(); public static ChessFlyWeight getChess(String color){ ChessFlyWeight chessFlyWeight = map.get(color); if (chessFlyWeight==null){ chessFlyWeight = new ConcreteChess(color); map.put(color,chessFlyWeight); } return chessFlyWeight; } } class Demo{ public static void main(String[] args) { ChessFlyWeight chess = ChessFlyWeightFactory.getChess("黑"); ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑"); System.out.println(chess==chess1); //非共享的变量从外部传递 chess.display(new Coordinate(1,1)); chess1.display(new Coordinate(2,2)); } }