OO设计原则:SOLID

OO设计原则:SOLID

OO指的是面向对象

【OO设计原则:SOLID】

  • (SRP) 单一责任原则
  • (OCP) 开放-封闭原则
  • (LSP) Liskov 替换原则
  • (DIP) 依赖转置原则
  • (ISP) 接口聚合原则

【单一责任原则SRP】

  • 含义:一个类改变的原因不应该超过一个。需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。

  • 不应有多于1个的原因使得一个类发生变化。

  • 一个类,一个责任。

  • 如果一个类包含了多个责任,那么将引起不良后果:引入额外的包,占据资源;导致频繁的重新配置、部署等。

  • SRP是最简单的原则,却是最难做好的原则。

  • SRP的一个反例:

    在这里插入图片描述

【(面向变化的)开放/封闭原则OCP】

  • 对扩展性的开放

    • 模块的行为应是可扩展的,从而该模块可表现出新的行为以满足需求的变化。
  • 对修改的封闭

    • 但模块自身的源代码是不应被修改
    • 扩展模块行为的一般途径是修改模块的内部实现
    • 如果一个模块不能被修改,那么它通常被认为是具有固定的行为
  • 关键解决方案:抽象技术。

    • “软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭”,也就是说,使用继承和组合/委托改变类的行为
  • OCP的一个例子:

(修改前)

在这里插入图片描述

(修改后)

在这里插入图片描述

【Liskov替换原则】

  • 子类型必须能够替换其基类型。

  • 派生类必须能够通过其基类的接口使用,客户端无需了解二者之间的差异。

  • Liskov’s 替换原则意思是:“子类型必须能够替换它们的基类型。“或者换个说法:”使用基类引用的地方必须能使用继承类的对象而不必知道它。” 这个原则正是保证继承能够被正确使用的前提。通常我们都说,“优先使用组合(委托)而不是继承”或者说“只有在确定是 is-a 的关系时才能使用继承”,因为继承经常导致”紧耦合“的设计。

【接口分离原则ISP】

  • 含义:不能强迫客户端依赖于它们不需要的接口,只提供必需的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
  • 客户端不应依赖于它们不需要的方法。
  • 避免接口(被大量方法)污染。
  • 避免”胖“接口。
  • “胖”接口具有很多缺点。具有“胖”接口的类是其接口没有内聚性的类(不够聚合)。
  • 胖接口可分解为多个小的接口;不同的接口向不同的客户端提供服务;客户端只访问自己所需要的端口。(而不是提供给客户模块一个大的接口)

【依赖转置原则DIP】

  • 定义:
    • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
    • 抽象不应该依赖于细节,细节应该依赖于抽象
  • 应该使用大量的接口和抽象
  • 依赖即委托。调用时/使用时尽量依赖接口不是实现类
  • 委托的时候要通过接口建立联系,而非具体的子类。(这样可以很容易切换方法)
  • 这个设计原则的亮点在于任何被DI框架注入的类很容易用mock对象进行测试和维护,因为对象创建代码集中在框架中,客户端代码也不混乱。有很多方式可以实现依赖倒置,比如像AspectJ等的AOP(Aspect Oriented programming)框架使用的字节码技术,或Spring框架使用的代理等。
    • 高层模块不要依赖低层模块;
    • 高层和低层模块都要依赖于抽象;
    • 抽象不要依赖于具体实现;
    • 具体实现要依赖于抽象;
    • 抽象和接口使模块之间的依赖分离。

【总结】

  • 抽象(abstraction):模块之间通过抽象隔离开来,将稳定部分和容易变化部分分开
    • LSP:对外界看来,父类和子类是“一样”的;
    • DIP:对接口编程,而不是对实现编程,通过抽象接口隔离变化;
    • OCP:当需要变化时,通过扩展隐藏在接口之后的子类加以完成,而不要修改接口本身。
  • 分离(Separation):Keep It Simple, Stupid (KISS)
    • SRP:按责任将大类拆分为多个小类,每个类完成单一职责,规避变化,提高复用度;
    • ISP:将接口拆分为多个小接口,规避不必要的耦合。
  • 归纳起来:让类保持责任单一、接口稳定。

语法驱动的构造

【语法驱动的构造】

  • 有一类应用,从外部读取文本数据,在应用中做进一步处理
  • 输入文件有特定格式,程序需读取文件并从中抽取正确的内容
  • 从网络上传输过来的消息,遵循特定的协议
  • 用户在命令行输入的指令,遵循特定的格式
  • 内存中存储的字符串,也有格式需要
  • 使用grammar判断字符串是否合法,并解析成程序里使用的数据结构
  • 通常是递归的数据结构

【语法】

  • 语法中的产物有这种形式:nonterminal ::= expression of terminals, nonterminals, and operators (这个 nonterminal 通常被称为根节点)

  • 产生式表达式中最重要的三个操作符:(注意::

    • 连接:x ::= y z
    • 重复:x ::= y* X匹配零个或多个y
    • 联合:x ::= y | z X匹配y或z
  • 其他操作符

    • ?表示出现0次或1次
    • +表示至少出现1次(= xx*
    • [...]表示包含方括号中列出的任何字符的长度为1的字符串
    • [^...]表示包含括号中未列出的任何字符长度为长度为1的字符串
  • * ? +优先级最高,连接次之,|最低

  • 终结符用单引号

  • 注意检查为空情况

【正则语法和正则表达式】

  • 正则语法:简化之后可以表达为一个产生式而不包含任何非终止节点。

  • 正则表达式去除引号和空格,从而表达更简洁(更难懂))

    markdown ::= ([^_]* | '_' [^_]* '_' )* 简化为markdown ::= ([^_]*|_[^_]*_)*

  • 正则表达式里的特殊操作符

    • .任何单个字符
    • \d任何数字(即[0-9]
    • \s任何空白字符,包括空格、制表符、换页符等
    • \w任何字符包括下划线 (等价于[a-zA-Z_0-9]
    • \转义运算符 ,使其与字面上匹配(在Java中写需要两个\\一个给Java语言检查,一个给语法解析用)

【在Java中使用正则表达式】

  • 适用场合:我们用正则表达式匹配字符串(例如String.split, String.matches, java.util.regex.Pattern

  • java.util.regex由以下三部分组成:

    • Pattern对象是对正则表达式进行编译之后得到的结果
    • Matcher对象:利用Pattern对输入字符串进行解析
    • PatternSyntaxException
  • 例子:

    • 用一个空格代替所有的多个空格:

      String singleSpacedString = string.replaceAll(" +", " ");
      
    • 匹配一个URL:

      Pattern regex = Pattern.compile("http://([a-z]+\\.)+[a-z]+(:[0-9]+)?/");
      Matcher m = regex.matcher(string);
      if (m.matches()) {
          // then string is a url
      }
      
    • 提取HTML标签的一部分:(匹配双引号用\"

      Pattern regex = Pattern.compile("<a href=\"([^\"]*)\">"); //可匹配如a href="(abc)">
      Matcher m = regex.matcher(string);
      if (m.matches()){
      	string url = m.group(1);
      	// Matcher.group(n) returns the nth parenthesized part of the regex
      }
      

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值