zoukankan      html  css  js  c++  java
  • java----设计模式--结构型模式(GOF23)

    核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

     适配器设计模式:

      将一个类的接口转换成客户希望的另一个的接口

      适配器使原本由于接口不兼容而不能工作的那些类可以一起工作

      适配器比较经典的案例

    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));
        }
    }
  • 相关阅读:
    MySQL 5.7.18 zip 文件安装过程
    Mysql 自定义随机字符串
    JAVA基本类型和引用类型
    初识JSP
    Java导出错误数据
    时序图的使用习惯
    Redis踩坑
    ES踩坑
    代码规范
    Git在公司的使用流程
  • 原文地址:https://www.cnblogs.com/yanxiaoge/p/11636025.html
Copyright © 2011-2022 走看看