码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式

时间:2021-06-20 18:03:24      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:解决   proxy   适配   sign   cep   工厂方法   中介   settime   维护   

什么是设计模式?

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是与法规定,而是一套用来提高代码复用性、可维护性、可读性、稳健性以及安全性的解决方案。

学习设计模式的意义

设计模式的本质是面型对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。

正确使用设计模式具有以下优点:

  • 可以提高程序员的思维能力、编程能力和设计能力;

  • 使程序设计更加标准化、代码编制更加工程化、使软件开发效率大大提高,从而缩短软件的开发周期。

  • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

设计模式的基本要素

  • 模式名称

  • 要解决的问题

  • 解决方案

  • 效果

GoF23

  • GoF23 一种思维、一种态度、一种进步

  • 创建型模式:(对象的创建与使用分离) 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式;

  • 结构型模式:(按照某种布局组成一种更大的结构) 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;

  • 行为型模式:(描述对象之间相互协作完成单个对象无法完成的任务,主要是分配一些职责) 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

面向对象(OOP)七大原则

设计模式必须遵守OOP原则

  • 开闭原则:对扩展开放,对修改关闭

  • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立(尽量不重写父类的方法,不要破坏继承关系)

  • 依赖倒置原则:要面向接口编程,不要面向实现编程(降低耦合性)

  • 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性(类的职责要单一)

  • 接口隔离原则:要为个各类建立它们需要的专用接口

  • 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话(降低耦合性)

  • 合成复用原则:尽量先使用组合或聚合等关联关系来实现,其次才考虑使用继承关系来实现

设计模式

创建型模式

作用: 整体而言创建者模式就是帮助我们创建(或克隆)对象

单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式;

单例模式

  • 核心作用: 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

  • 常见场景: windows的任务管理器

    windows的回收站

    项目中,读取配置文件的类

    网站计数器一般也会采用单例模式,可以保证同步

    数据库连接池的设计一般也是单例模式

    在servlet编程中,每个servlet也是单例的

    在spring中每一个bean默认就是单例的

    。。。。。

工厂模式

作用

  • 实现了创建者和调用者分离

核心本质

  • 实例化对象不适用new,用工厂方法代替

  • 将选择实现类,创建对象统一管理和控制。从而将调用者跟实现类解耦

详细分类

  • 简单工厂模式(静态工厂模式) 用来生产同一等级结构中的任意产品(增加新的产品需要扩展已有代码)

  • 工厂方法模式 用来生产同一等级机构中的固定产品

  • 抽象工厂模式 围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂

小结

  • 简单工厂模式(又称静态工厂模式) 虽然某种程度上不符合设计原则,单实际使用最多

  • 工厂方法模式 不修改已有类的前提下,通过增加新的工厂类实现扩展

  • 抽象工厂模式 不可以增加产品,可以增加产品簇

应用场景

  • jdk中Calendar的getInstance方法

  • jdbc中Connection对象的获取

  • spring中ioc容器创建管理bean对象

  • 反射中Class对象的newInstance

抽象工厂模式

定义:抽象工厂模式提供一个常见一系列相关或者相互依赖对象的接口,无需指定他们具体的类

适用场景

  • 客户端(应用层)不依赖于产品类实例如何创建、实现等细节

  • 强调一系列相关的产品对象(属于同一产品簇)一起使用创建对象需要大量的重复代码

  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

优点

  • 具体产品在应用层的代码隔离,无需关心创建的细节

  • 将一个系列的产品统一到一起创建

缺点

  • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难

  • 增加了系统的抽象性和理解难度
    技术图片

建造者模式

建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

主要作用:在用户不知道对象的创造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

例子

  • 工厂(建造者模式):负责制造汽车(组装过程和细节都在工厂内)

  • 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的)

角色分析:

应用场景

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;

  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品;

  • 适合于一个具有比较多的零件(属性)的产品(对象)的创建过程;

建造者模式与抽象工厂模式的比较

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品为与不同的产品等级结构,构成了一个产品簇;

  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者来知道如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象;

  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品簇的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车;

原型模式

原型模式实际上就是以一个对象为原型进行克隆

浅克隆

原型对象

import java.util.Date;
?
/**
* 视频
*
* 1、实现Cloneable接口
* 2、重写clone()方法
*/
public class Video implements Cloneable{
   private String name;
   private Date createTime;

   // setter、getter、toString 略···
?
   @Override
   protected Object clone() throws CloneNotSupportedException {
       return super.clone();
  }
}
?

测试

?
import java.util.Date;
?
/**
* 客户端:克隆(浅克隆)
*/
public class Client {
   public static void main(String[] args) throws CloneNotSupportedException {
       Date createTime = new Date();
       //原型对象 v1
       Video v1 = new Video("狂神说java", createTime);
       //通过 v1 克隆出 v2
       Video v2 = (Video) v1.clone();
       
       System.out.println("v1=>" + v1);
       System.out.println("v2=>" + v2);
       
       System.out.println("==============================================");
       
       createTime.setTime(1111111);
       System.out.println("v1=>" + v1);
       System.out.println("v2=>" + v2);
  }
}

克隆出来的对象和原来的对象一模一样,弊端是当修改了 他们的公共对象 createTime 时 v1 和 v2 的 createTime 都会随之改变

 

深克隆

原型(在 clone() 方法中将属性也进行克隆)

import java.util.Date;
?
/**
* 视频
*
* 1、实现Cloneable接口
* 2、重写clone()方法
*/
public class Video implements Cloneable{
   private String name;
   private Date createTime;

   // setter、getter、toString 略···
?
   @Override
   protected Object clone() throws CloneNotSupportedException {
       Object obj = super.clone();
?
       //实现深克隆
       Video v = (Video) obj;
       //将这个对象的属性也进行克隆
       v.createTime = (Date) this.createTime.clone();
       return v;
  }
}
?

测试

import java.util.Date;
?
/**
* 客户端:克隆(深克隆,改造 Video 中的 clone 方法)
*/
public class Client {
   public static void main(String[] args) throws CloneNotSupportedException {
       Date createTime = new Date();
       //原型对象 v1
       Video v1 = new Video("狂神说java", createTime);
       System.out.println("v1=>" + v1);
?
       //通过 v1 克隆出 v2
       Video v2 = (Video) v1.clone();
       System.out.println("v2=>" + v2);
?
       System.out.println("==============================================");
       createTime.setTime(1111111);
       System.out.println("v1=>" + v1);
       System.out.println("v2=>" + v2);
  }
}
?

通过测试可以看出深克隆可以保证对象的安全性。

结构型模式

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

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;

适配器模式

作用:将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容二不能一起工作的类可以在一起工作

角色分析

目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。

需要适配的类:需要适配的类或适配者类。

适配器:通过包装一个需要适配的对象,把原接口转换成目标对象。

适配器模式分为对象适配器和类适配器

对象适配器(组合的方式)优点

  • 一个对象适配器可一个把多个不同的适配者适配到同一个目标

  • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏替换原则”,适配者的子类也可通过该适配器进行适配

类适配器缺点

  • 对于Java、C#等不支持多继承的语言,一次最多只能适配一个适配者,不能同时适配多个适配者

  • 在Java、C#等语言中,类适配模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性

适配场景

  • 系统需要使用一些现有的类,而这些接口(如方法名)不符合系统的需要,甚至没有这些类的源代码

  • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作

桥接模式bridge

桥接模式时将抽象部分与它的实现部分分离,使它们都可以独立的变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

技术图片

好处分析

  • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式时比多继承方案更好的解决方法,加大的减少了子类的个数,从而降低管理和维护的成本

  • 桥接模式提高了系统的可扩充性,在两个变化纬度中任意扩展一个纬度,都不需要修改原有的系统,符合开闭原则,就像一座桥,可以吧连个变化的纬度连接起来

劣势分析

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程

  • 桥接模式要求正确识别出系统中两个独立变化的纬度,因此其适用范围具有一定的局限性

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层

代理模式分类:

  • 静态代理

  • 动态代理

静态代理

技术图片

代码步骤

  1. 接口

    /**
    * 租房
    */
    public interface Rent {
       void rent();
    }
  2. 真实角色

    /**
    * 房东
    */
    public class Landlord implements Rent{
       @Override
       public void rent() {
           System.out.println("房东出租房子");
      }
    }
  3. 代理角色

    /**
    * 代理角色:中介
    */
    public class Proxy implements Rent{
    ?
       private Landlord landlord;
     
       public Proxy(Landlord landlord) {
           this.landlord = landlord;
      }
    ?
       @Override
       public void rent() {
           System.out.print("代理帮");
           landlord.rent();
           seeHouse();
           contract();
           fare();
      }
    ?
       public void seeHouse() {
           System.out.println("中介带你看房");
      }
    ?
       public void contract() {
           System.out.println("签租赁合同");
      }
    ?
       public void fare() {
           System.out.println("中介收中介费");
      }
    }
  4. 客户端访问代理角色

    /**
    * 客户要租房子
    */
    public class Client {
       public static void main(String[] args) {
           Proxy proxy = new Proxy(new Landlord());
           proxy.rent();
      }
    }

     

角色分析

  • 抽象角色:一般使用接口或抽象类解决

  • 真是角色:被代理的角色

  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

  • 客户:访问代理对象的人

静态代理的好处

  • 可以使真实角色的操作更加纯粹,不用去专注一些公共的业务

  • 公共的业务就交给了代理角色,实现了分工

  • 公共的业务发生扩展的时候方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

动态代理

动态代理和静态代理角色一样,动态代理的代理类时动态生成的,不是我们直接写好的

动态代理分为:

  • 基于接口的动态代理(JKD动态代理)

  • 基于类的动态代理(cglib)

  • 基于Java字节码(JAVAssist)

需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序

动态代理的好处

  • 可以使真实角色的操作更加纯粹,不用去专注一些公共的业务

  • 公共的业务就交给了代理角色,实现了分工

  • 公共的业务发生扩展的时候方便集中管理

  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务

  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

设计模式

标签:解决   proxy   适配   sign   cep   工厂方法   中介   settime   维护   

原文地址:https://www.cnblogs.com/kaisheng-reflect/p/14905994.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!