范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

常见代码重构技巧(非常实用)

  为什么要重构
  1_代码重构漫画.jpeg
  项目在不断演进过程中,代码不停地在堆砌。如果没有人为代码的质量负责,代码总是会往越来越混乱的方向演进。当混乱到一定程度之后,量变引起质变,项目的维护成本已经高过重新开发一套新代码的成本,想要再去重构,已经没有人能做到了。
  造成这样的原因往往有以下几点:  编码之前缺乏有效的设计  成本上的考虑,在原功能堆砌式编程  缺乏有效代码质量监督机制
  对于此类问题,业界已有有很好的解决思路:通过持续不断的重构将代码中的"坏味道"清除掉。  什么是重构
  重构一书的作者Martin Fowler对重构的定义:
  重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
  重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
  根据重构的规模可以大致分为大型重构和小型重构:
  大型重构 :对顶层代码设计的重构,包括:系统、模块、代码结构、类与类之间的关系等的重构,重构的手段有:分层、模块化、解耦、抽象可复用组件等等。这类重构的工具就是我们学习过的那些设计思想、原则和模式。这类重构涉及的代码改动会比较多,影响面会比较大,所以难度也较大,耗时会比较长,引入bug的风险也会相对比较大。
  小型重构 :对代码细节的重构,主要是针对类、函数、变量等代码级别的重构,比如规范命名和注释、消除超大类或函数、提取重复代码等等。小型重构更多的是使用统一的编码规范。这类重构要修改的地方比较集中,比较简单,可操作性较强,耗时会比较短,引入bug的风险相对来说也会比较小。什么时候重构 新功能开发、修bug或者代码review中出现"代码坏味道",我们就应该及时进行重构。持续在日常开发中进行小重构,能够降低重构和测试的成本。 代码的坏味道
  2_代码常见问题.png
  代码重复  实现逻辑相同、执行流程相同
  方法过长  方法中的语句不在同一个抽象层级  逻辑难以理解,需要大量的注释  面向过程编程而非面向对象
  过大的类  类做了太多的事情  包含过多的实例变量和方法  类的命名不足以描述所做的事情
  逻辑分散  发散式变化:某个类经常因为不同的原因在不同的方向上发生变化  散弹式修改:发生某种变化时,需要在多个类中做修改
  严重的情结依恋  某个类的方法过多的使用其他类的成员
  数据泥团/基本类型偏执  两个类、方法签名中包含相同的字段或参数  应该使用类但使用基本类型,比如表示数值与币种的Money类、起始值与结束值的Range类
  不合理的继承体系  继承打破了封装性,子类依赖其父类中特定功能的实现细节  子类必须跟着其父类的更新而演变,除非父类是专门为了扩展而设计,并且有很好的文档说明
  过多的条件判断
  过长的参数列
  临时变量过多
  令人迷惑的暂时字段  某个实例变量仅为某种特定情况而设置  将实例变量与相应的方法提取到新的类中
  纯数据类  仅包含字段和访问(读写)这些字段的方法  此类被称为数据容器,应保持最小可变性
  不恰当的命名  命名无法准确描述做的事情  命名不符合约定俗称的惯例
  过多的注释  坏代码的问题难以复用  系统关联性过多,导致很难分离可重用部分  难于变化  一处变化导致其他很多部分的修改,不利于系统稳定  难于理解  命名杂乱,结构混乱,难于阅读和理解  难以测试  分支、依赖较多,难以覆盖全面  什么是好代码
  3_代码质量如何衡量.jpg
  代码质量的评价有很强的主观性,描述代码质量的词汇也有很多,比如可读性、可维护性、灵活、优雅、简洁。这些词汇是从不同的维度去评价代码质量的。其中,可维护性、可读性、可扩展性又是提到最多的、最重要的三个评价标准。
  要写出高质量代码,我们就需要掌握一些更加细化、更加能落地的编程方法论,这就包含面向对象设计思想、设计原则、设计模式、编码规范、重构技巧等。  如何重构SOLID原则
  4_SOLID原则.png  单一职责原则
  一个类只负责完成一个职责或者功能,不要存在多于一种导致类变更的原因。
  单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、松耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。  开放-关闭原则
  添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。
  开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。
  很多设计原则、设计思想、设计模式,都是以提高代码的扩展性为最终目的的。特别是 23 种经典设计模式,大部分都是为了解决代码的扩展性问题而总结出来的,都是以开闭原则为指导原则的。最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)。  里氏替换原则
  子类对象(object of subtype/derived class)能够替换程序(program)中父类对象(object of base/parent class)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。
  子类可以扩展父类的功能,但不能改变父类原有的功能
  父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。   接口隔离原则
  调用方不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。  依赖反转原则
  高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。  迪米特法则
  一个对象应该对其他对象保持最少的了解  合成复用原则
  尽量使用合成/聚合的方式,而不是使用继承。
  单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,告诉我们要对扩展开放,对修改关闭。  设计模式
  设计模式:软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案。   创建型 :主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码 结构型 :主要通过类或对象的不同组合,解耦不同功能的耦合 行为型 :主要解决的是类或对象之间的交互行为的耦合
  代码分层
  image.png
  模块结构说明  server_main:配置层,负责整个项目的module管理,maven配置管理、资源管理等;  server_application:应用接入层,承接外部流量入口,例如:RPC接口实现、消息处理、定时任务等;不要在此包含业务逻辑;  server_biz:核心业务层,用例服务、领域实体、领域事件等  server_irepository:资源接口层,负责资源接口的暴露  server_repository:资源层,负责资源的proxy访问,统一外部资源访问,隔离变化。注意:这里强调的是弱业务性,强数据性;  server_common:公共层,vo、工具等
  代码开发要遵守各层的规范,并注意层级之间的依赖关系。  命名规范一个好的命名应该要满足以下两个约束:
  准确描述所做得事情
  格式符合通用的惯例
  如果你觉得一个类或方法难以命名的时候,可能是其承载的功能太多了,需要进一步拆分。   约定俗称的惯例
  类命名
  类名使用大驼峰命名形式,类命通常使用名词或名词短语。接口名除了用名词和名词短语以外,还可以使用形容词或形容词短语,如 Cloneable,Callable 等,表示实现该接口的类有某种功能或能力。
  方法命名
  方法命名采用小驼峰的形式,首字小写,往后的每个单词首字母都要大写。和类名不同的是,方法命名一般为动词或动词短语,与参数或参数名共同组成动宾短语,即动词 + 名词。一个好的函数名一般能通过名字直接获知该函数实现什么样的功能。
  重构技巧提炼方法
  多个方法代码重复、方法中代码过长或者方法中的语句不在一个抽象层级。
  方法是代码复用的最小粒度,方法过长不利于复用,可读性低,提炼方法往往是重构工作的第一步。
  意图导向编程 :把处理某件事的流程和具体做事的实现方式分开。 把一个问题分解为一系列功能性步骤,并假定这些功能步骤已经实现  我们只需把把各个函数组织在一起即可解决这一问题  在组织好整个功能后,我们在分别实现各个方法函数  /**    * 1、交易信息开始于一串标准ASCII字符串。    * 2、这个信息字符串必须转换成一个字符串的数组,数组存放的此次交易的领域语言中所包含的词汇元素(token)。    * 3、每一个词汇必须标准化。    * 4、包含超过150个词汇元素的交易,应该采用不同于小型交易的方式(不同的算法)来提交,以提高效率。    * 5、如果提交成功,API返回"true";失败,则返回"false"。    */ public class Transaction {       public Boolean commit(String command) {             Boolean result = true;             String[] tokens = tokenize(command);             normalizeTokens(tokens);             if (isALargeTransaction(tokens)) {                   result = processLargeTransaction(tokens);             } else {                   result = processSmallTransaction(tokens);             }             return result;       } } 以函数对象取代函数
  将函数放进一个单独对象中,如此一来局部变量就变成了对象内的字段。然后你可以在同一个对象中将这个大型函数分解为多个小型函数。  引入参数对象
  方法参数比较多时,将参数封装为参数对象  移除对参数的赋值public int discount(int inputVal, int quantity, int yearToDate) {   if (inputVal > 50) inputVal -= 2;   if (quantity > 100) inputVal -= 1;   if (yearToDate > 10000) inputVal -= 4;   return inputVal; }  public int discount(int inputVal, int quantity, int yearToDate) {    int result = inputVal;   if (inputVal > 50) result -= 2;    if (quantity > 100) result -= 1;    if (yearToDate > 10000) result -= 4;    return result;  } 将查询与修改分离
  任何有返回值的方法,都不应该有副作用  不要在convert中调用写操作,避免副作用  常见的例外:将查询结果缓存到本地  移除不必要临时变量
  临时变量仅使用一次或者取值逻辑成本很低的情况下  引入解释性变量
  将复杂表达式(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途  if ((platform.toUpperCase().indexOf("MAC") > -1)      && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0) {      // do something  }     final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;  final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;  final boolean wasResized = resize > 0;  if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {      // do something  } 使用卫语句替代嵌套条件判断
  把复杂的条件表达式拆分成多个条件表达式,减少嵌套。嵌套了好几层的if - then-else语句,转换为多个if语句  //未使用卫语句 public void getHello(int type) {     if (type == 1) {         return;     } else {         if (type == 2) {             return;         } else {             if (type == 3) {                 return;             } else {                 setHello();             }         }     } }   //使用卫语句 public void getHello(int type) {     if (type == 1) {         return;     }     if (type == 2) {         return;     }     if (type == 3) {         return;     }     setHello(); } 使用多态替代条件判断断
  当存在这样一类条件表达式,它根据对象类型的不同选择不同的行为。可以将这种表达式的每个分支放进一个子类内的复写函数中,然后将原始函数声明为抽象函数。  public int calculate(int a, int b, String operator) {     int result = Integer.MIN_VALUE;       if ("add".equals(operator)) {         result = a + b;     } else if ("multiply".equals(operator)) {         result = a * b;     } else if ("pide".equals(operator)) {         result = a / b;     } else if ("subtract".equals(operator)) {         result = a - b;     }     return result; }
  当出现大量类型检查和判断时,if else(或switch)语句的体积会比较臃肿,这无疑降低了代码的可读性。 另外,if else(或switch)本身就是一个"变化点",当需要扩展新的类型时,我们不得不追加if else(或switch)语句块,以及相应的逻辑,这无疑降低了程序的可扩展性,也违反了面向对象的开闭原则。
  基于这种场景,我们可以考虑使用"多态"来代替冗长的条件判断,将if else(或switch)中的"变化点"封装到子类中。这样,就不需要使用if else(或switch)语句了,取而代之的是子类多态的实例,从而使得提高代码的可读性和可扩展性。很多设计模式使用都是这种套路,比如策略模式、状态模式。   public interface Operation {    int apply(int a, int b);  }  public class Addition implements Operation {    @Override    public int apply(int a, int b) {      return a + b;    }  }  public class OperatorFactory {     private final static Map operationMap = new HashMap<>();     static {         operationMap.put("add", new Addition());         operationMap.put("pide", new Division());         // more operators     }       public static Operation getOperation(String operator) {         return operationMap.get(operator);     } }  public int calculate(int a, int b, String operator) {     if (OperatorFactory .getOperation == null) {        throw new IllegalArgumentException("Invalid Operator");     }     return OperatorFactory .getOperation(operator).apply(a, b); } 使用异常替代返回错误码
  非正常业务状态的处理,使用抛出异常的方式代替返回错误码  不要使用异常处理用于正常的业务流程控制  异常处理的性能成本非常高  尽量使用标准异常  避免在finally语句块中抛出异常  如果同时抛出两个异常,则第一个异常的调用栈会丢失  finally块中应只做关闭资源这类的事情  //使用错误码 public boolean withdraw(int amount) {     if (balance < amount) {         return false;     } else {         balance -= amount;         return true;     } }  //使用异常 public void withdraw(int amount) {     if (amount > balance) {         throw new IllegalArgumentException("amount too large");         }     balance -= amount; } 引入断言
  某一段代码需要对程序状态做出某种假设,以断言明确表现这种假设。  不要滥用断言,不要使用它来检查"应该为真"的条件,只使用它来检查"一定必须为真"的条件  如果断言所指示的约束条件不能满足,代码是否仍能正常运行?如果可以就去掉断言  引入Null对象或特殊对象
  当使用一个方法返回的对象时,而这个对象可能为空,这个时候需要对这个对象进行操作前,需要进行判空,否则就会报空指针。当这种判断频繁的出现在各处代码之中,就会影响代码的美观程度和可读性,甚至增加Bug的几率。
  空引用的问题在Java中无法避免,但可以通过代码编程技巧(引入空对象)来改善这一问题。  //空对象的例子 public class OperatorFactory {    static Map operationMap = new HashMap<>();    static {      operationMap.put("add", new Addition());      operationMap.put("pide", new Division());      // more operators    }    public static Optional getOperation(String operator) {      return Optional.ofNullable(operationMap.get(operator));    }  }  public int calculate(int a, int b, String operator) {    Operation targetOperation = OperatorFactory.getOperation(operator)       .orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));    return targetOperation.apply(a, b);  }  //特殊对象的例子 public class InvalidOp implements Operation {    @Override    public int apply(int a, int b)  {      throw new IllegalArgumentException("Invalid Operator");   }  } 提炼类
  根据单一职责原则,一个类应该有明确的责任边界。但在实际工作中,类会不断的扩展。当给某个类添加一项新责任时,你会觉得不值得分离出一个单独的类。于是,随着责任不断增加,这个类包含了大量的数据和函数,逻辑复杂不易理解。
  此时你需要考虑将哪些部分分离到一个单独的类中,可以依据高内聚低耦合的原则。如果某些数据和方法总是一起出现,或者某些数据经常同时变化,这就表明它们应该放到一个类中。另一种信号是类的子类化方式:如果你发现子类化只影响类的部分特性,或者类的特性需要以不同方式来子类化,这就意味着你需要分解原来的类。  //原始类 public class Person {     private String name;     private String officeAreaCode;     private String officeNumber;      public String getName() {         return name;     }      public String getTelephoneNumber() {         return ("(" + officeAreaCode + ")" + officeNumber);     }      public String getOfficeAreaCode() {         return officeAreaCode;     }      public void setOfficeAreaCode(String arg) {         officeAreaCode = arg;     }      public String getOfficeNumber() {         return officeNumber;     }      public void setOfficeNumber(String arg) {         officeNumber = arg;     } }  //新提炼的类(以对象替换数据值) public class TelephoneNumber {     private String areaCode;     private String number;      public String getTelephnoeNumber() {         return ("(" + getAreaCode() + ")" + number);     }      String getAreaCode() {         return areaCode;     }      void setAreaCode(String arg) {         areaCode = arg;     }      String getNumber() {         return number;     }      void setNumber(String arg) {         number = arg;     } } 组合优先于继承
  继承使实现代码重用的有力手段,但这并非总是完成这项工作的最佳工具,使用不当会导致软件变得很脆弱。与方法调用不同的是,继承打破了封装性。子类依赖于其父类中特定功能的实现细节,如果父类的实现随着发行版本的不同而变化,子类可能会遭到破坏,即使他的代码完全没有改变。
  举例说明,假设有一个程序使用HashSet,为了调优该程序的性能,需要统计HashSet自从它创建以来添加了多少个元素。为了提供该功能,我们编写一个HashSet的变体。  // Inappropriate use of inheritance! public class InstrumentedHashSet extends HashSet {     // The number of attempted element insertions     private int addCount = 0;      public InstrumentedHashSet() { }      public InstrumentedHashSet(int initCap, float loadFactor) {         super(initCap, loadFactor);     }      @Override     public boolean add(E e) {         addCount++;         return super.add(e);     }      @Override     public boolean addAll(Collection<? extends E> c) {         addCount += c.size();         return super.addAll(c);     }      public int getAddCount() {         return addCount;     } }
  通过在新的类中增加一个私有域,它引用现有类的一个实例,这种设计被称为组合,因为现有的类变成了新类的一个组件。这样得到的类将会非常稳固,它不依赖现有类的实现细节。即使现有的类添加了新的方法,也不会影响新的类。许多设计模式使用就是这种套路,比如代理模式、装饰者模式  // Reusable forwarding class public class ForwardingSet implements Set {     private final Set s;     public ForwardingSet(Set s) { this.s = s; }        @Override     public int size() { return s.size(); }     @Override     public boolean isEmpty() { return s.isEmpty(); }     @Override     public boolean contains(Object o) { return s.contains(o); }     @Override     public Iterator iterator() { return s.iterator(); }     @Override     public Object[] toArray() { return s.toArray(); }     @Override     public  T[] toArray(T[] a) { return s.toArray(a); }     @Override     public boolean add(E e) { return s.add(e); }     @Override     public boolean remove(Object o) { return s.remove(o); }     @Override     public boolean containsAll(Collection<?> c) { return s.containsAll(c); }     @Override     public boolean addAll(Collection<? extends E> c) { return s.addAll(c); }     @Override     public boolean retainAll(Collection<?> c) { return s.retainAll(c); }     @Override     public boolean removeAll(Collection<?> c) { return s.removeAll(c); }     @Override     public void clear() { s.clear(); } }  // Wrappter class - uses composition in place of inheritance public class InstrumentedHashSet extends ForwardingSet {     private int addCount = 0;      public InstrumentedHashSet1(Set s) {         super(s);     }      @Override     public boolean add(E e) {         addCount++;         return super.add(e);     }      @Override     public boolean addAll(Collection<? extends E> c) {         addCount += c.size();         return super.addAll(c);     }      public int getAddCount() {         return addCount;     } }
  继承与组合如何取舍  只有当子类真正是父类的子类型时,才适合继承。对于两个类A和B,只有两者之间确实存在"is-a"关系的时候,类B才应该继承A;  在包的内部使用继承是非常安全的,子类和父类的实现都处在同一个程序员的控制之下;  对于专门为了继承而设计并且具有很好的文档说明的类来说,使用继承也是非常安全的;  其他情况就应该优先考虑组合的方式来实现  接口优于抽象类
  Java提供了两种机制,可以用来定义允许多个实现的类型:接口和抽象类。自从Java8为接口增加缺省方法(default method),这两种机制都允许为实例方法提供实现。主要区别在于,为了实现由抽象类定义的类型,类必须称为抽象类的一个子类。因为Java只允许单继承,所以用抽象类作为类型定义受到了限制。
  接口相比于抽象类的优势:  现有的类可以很容易被更新,以实现新的接口。  接口是定义混合类型(比如Comparable)的理想选择。  接口允许构造非层次结构的类型框架。
  接口虽然提供了缺省方法,但接口仍有有以下局限性:  接口的变量修饰符只能是public static final的  接口的方法修饰符只能是public的  接口不存在构造函数,也不存在this  可以给现有接口增加缺省方法,但不能确保这些方法在之前存在的实现中都能良好运行。  因为这些默认方法是被注入到现有实现中的,它们的实现者并不知道,也没有许可
  接口缺省方法的设计目的和优势在于:
  为了接口的演化  Java 8 之前我们知道,一个接口的所有方法其子类必须实现(当然,这个子类不是一个抽象类),但是 java 8 之后接口的默认方法可以选择不实现,如上的操作是可以通过编译期编译的。这样就避免了由 Java 7 升级到 Java 8 时项目编译报错了。Java8在核心集合接口中增加了许多新的缺省方法,主要是为了便于使用lambda。
  可以减少第三方工具类的创建  例如在 List 等集合接口中都有一些默认方法,List 接口中默认提供 replaceAll(UnaryOperator)、sort(Comparator)、、spliterator()等默认方法,这些方法在接口内部创建,避免了为了这些方法而专门去创建相应的工具类。
  可以避免创建基类  在 Java 8 之前我们可能需要创建一个基类来实现代码复用,而默认方法的出现,可以不必要去创建基类。
  由于接口的局限性和设计目的的不同,接口并不能完全替换抽象类。但是通过对接口提供一个抽象的骨架实现类,可以把接口和抽象类的优点结合起来。  接口负责定义类型,或许还提供一些缺省方法,而骨架实现类则负责实现除基本类型接口方法之外,剩下的非基本类型接口方法。扩展骨架实现占了实现接口之外的大部分工作。这就是模板方法(Template Method)设计模式。
  Image [5].png
  接口Protocol:定义了RPC协议层两个主要的方法,export暴露服务和refer引用服务
  抽象类AbstractProtocol:封装了暴露服务之后的Exporter和引用服务之后的Invoker实例,并实现了服务销毁的逻辑
  具体实现类XxxProtocol:实现export暴露服务和refer引用服务具体逻辑  优先考虑泛型
  声明中具有一个或者多个类型参数(type parameter)的类或者接口,就是泛型(generic)类或者接口。泛型类和接口统称为泛型(generic type)。泛型从Java 5引入,提供了编译时类型安全检测机制。泛型的本质是参数化类型,通过一个参数来表示所操作的数据类型,并且可以限制这个参数的类型范围。泛型的好处就是编译期类型检测,避免类型转换。  // 比较三个值并返回最大值 public static > T maximum(T x, T y, T z) {      T max = x;    // 假设x是初始最大值      if ( y.compareTo( max ) > 0 ) {           max = y; //y 更大     }   if ( z.compareTo( max ) > 0 ) {          max = z; // 现在 z 更大                 }   return max; // 返回最大对象 }  public static void main( String args[] ) {      System.out.printf( "%d, %d 和 %d 中最大的数为 %d  ",  3, 4, 5, maximum( 3, 4, 5 ));      System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f  ",  6.6, 8.8, 7.7,  maximum( 6.6, 8.8, 7.7 ));      System.out.printf( "%s, %s 和 %s 中最大的数为 %s ","pear", "apple", "orange", maximum( "pear", "apple", "orange" ) ); } 不要使用原生态类型
  由于为了保持Java代码的兼容性,支持和原生态类型转换,并使用擦除机制实现的泛型。但是使用原生态类型就会失去泛型的优势,会受到编译器警告。  要尽可能地消除每一个非受检警告
  每一条警告都表示可能在运行时抛出ClassCastException异常。要尽最大的努力去消除这些警告。如果无法消除但是可以证明引起警告的代码是安全的,就可以在尽可能小的范围中,使用@SuppressWarnings("unchecked")注解来禁止警告,但是要把禁止的原因记录下来。  利用有限制通配符来提升API的灵活性
  参数化类型不支持协变的,即对于任何两个不同的类型Type1和Type2而言,List既不是List的子类型,也不是它的超类。为了解决这个问题,提高灵活性,Java提供了一种特殊的参数化类型,称作有限制的通配符类型,即List<? extends E>和List<? super E>。使用原则是producer-extends,consumer-super(PECS)。如果即是生产者,又是消费者,就没有必要使用通配符了。
  还有一种特殊的无限制通配符List<?>,表示某种类型但不确定。常用作泛型的引用,不可向其添加除Null以外的任何对象。  //List<? extends E> // Number 可以认为 是Number 的 "子类" List<? extends Number> numberArray = new ArrayList();  // Integer 是 Number 的子类 List<? extends Number> numberArray = new ArrayList();  // Double 是 Number 的子类 List<? extends Number> numberArray = new ArrayList();    //List<? super E> // Integer 可以认为是 Integer 的 "父类" List<? super Integer> array = new ArrayList();、 // Number 是 Integer 的 父类 List<? super Integer> array = new ArrayList(); // Object 是 Integer 的 父类 List<? super Integer> array = new ArrayList();  public static  void copy(List<? super T> dest, List<? extends T> src) {       int srcSize = src.size();       if (srcSize > dest.size())            throw new IndexOutOfBoundsException("Source does not fit in dest");       if (srcSize < COPY_THRESHOLD || (src instanceof RandomAccess && dest instanceof RandomAccess)) {             for (int i=0; i di=dest.listIterator();             ListIterator<? extends T> si=src.listIterator();             for (int i=0; i 运行所有测试并检查测试结果 -> 编写代码以通过测试 -> 运行所有测试且全部通过 -> 重构代码,以消除重复设计,优化设计结构  两个基本的原则仅在测试失败时才编写代码并且只编写刚好使测试通过的代码  编写下一个测试之前消除现有的重复设计,优化设计结构
  关注点分离是这两条规则隐含的另一个非常重要的原则。其表达的含义指在编码阶段先达到代码"可用"的目标,在重构阶段再追求"整洁"目标,每次只关注一件事!  分层测试点
  参考资料重构-改善既有代码的设计  设计模式  Effective Java  敏捷软件开发与设计的最佳实践  实现模式  测试驱动开发
  作者:VectorJin
谁能解决电池的问题,谁就能成为下一个世界首富,你认同吗?雷军曾经在一档节目当中表示过,他到了40岁才悟出了一个道理有些人很聪明,有些人很勤奋,但为什么最终没成功,是因为他们没有顺势而为。从现实来看,很多成功的人确实都是顺势而为,他们抓住嘴,是别人的路,是自己的点击上方蓝字关注,晴风每天和你一起剖析情感知识。文晴风图网络(若有侵权,请联系删除)莎士比亚曾说一千个观众,就有以前和哈姆雷特。说的是一千个人,有一千种看待的方式。生活中也是如此,毛主席为何坚持抗美援朝?美国1971年才醒悟,伟人真是高瞻远瞩1950年,美国总统杜鲁门悍然下令,插手朝鲜半岛的战争,为美国争取更大的国际利益。负责领导美军的麦克阿瑟,通过仁川登陆迅速扭转局势,赢得了一系列军事胜利。美军的节节胜利,给朝鲜造成钱学森回国,你知道当时中国为了和美国做交换,付出多大代价吗?我国在二十一世纪初期在世界上的地位已经让人刮目相看了。这是我们国人的骄傲!我国的经济军事都得到了长足发展,特别是在军事领域,我国的发展更是让外国人感到震惊。比如我国已有了自己的航空122年前,他提议卖掉中国这两个省,面积达288万平方公里自古以来,领土都是一个国家必不可少的组成部分。对于今天的我们来说,当涉及到国家领土主权问题时,更是必须要坚持寸土必争的理念。但是,在清朝时期,却有人向皇帝提出通过售卖国家疆土来筹集美媒科幻世界内外,元宇宙都几乎没有意义美国大西洋月刊网站10月21日发表题为元宇宙是坏东西的文章,作者为伊恩博格斯特。全文摘编如下在科幻小说中,世界末日是件大事。要么是气候崩溃,要么是外星人入侵迫使人类乘坐宇宙方舟逃离科学家称地球正在死亡,人类要何去何从?或许只有一个方向不知不觉,2021年再有两个月就要结束了,在过去的十个月,有关气候变化的讨论一直没有停止。越来越多的气候变化不断向我们发出警示地球正在加速走向死亡。或许不少的朋友还记得2020年发6。8mm超薄VR眼镜面世,它将如何加速元宇宙进程?科幻作家尼尔斯蒂芬森也许不会想到,自己在1992年出版的科幻小说雪崩为英文世界带来的两个新词Avater与Metaverse,会在21世纪再次成为科技与人文的前沿。其中,Avate优酷已支持苹果iOS15。1SharePlay(同播共享)功能iOS版优酷迎来10。1。8更新,新增了iOS15所支持的SharePlay(同播共享)功能。iOSiPadOS15。1正式版已支持同播共享功能,据苹果官方介绍,该功能允许在Fac苹果自研基带芯片有望2023年亮相,或集成在iPhoneSoC中集微网消息据DigiTimes报道,2022年将是高通为iPhone机型提供所有基带芯片的最后一年,预计2023年iPhone将开始采用苹果自己设计的5G基带芯片。早在5月份,苹果性能超越麒麟9000骁龙888!联发科全新旗舰芯片发布一直以来,芯片都是全球网友热议的话题,我们生活中所使用的智能设备,基本都与芯片息息相关。电脑手机平板等等,都离不了芯片的驱动。要说2021年主流旗舰手机使用的芯片,无非就是骁龙88
动物的热土,孩子的乐园汉密尔顿动物园位于新西兰汉密尔顿的西部郊区,占地面积25公顷。动物园里有93种400多只动物,其中鸟类达50多种。12月初夏的新西兰阳光灿烂,浓荫蔽天,今天,一家人来到汉密尔顿动物和最大的哺乳动物一起游泳是什么体验?春天和夏天是世界上许多地方观鲸的理想季节,如阿拉斯加冰岛加拿大等。让我们来看看这些寻找鲸鱼的最佳地点。美国,阿拉斯加州鲸鱼种类灰鲸座头鲸小须鲸逆戟鲸或虎鲸白鲸。最佳观鲸时间八月是最小米12Ultra真机图再曝光,这外观也太奇特了吧网传小米12超大杯将于7月份发布,这次小米12U堆料非常足,特别是有徕卡的超级影像系统和台积电版本的骁龙8Gen1加持,显得非常能打,由于各家下半年都有重磅旗舰,下半年应该又是机圈糖尿病病人是否应忌食动物内脏和动物血?不是。动物内脏类如肝肾心和动物血,含有丰富的脂溶性维生素B族维生素铁硒锌等,适量食用,对糖尿病病人健康有益。某些糖尿病病人因害怕动物内脏影响血脂,长期忌食畜禽肉类的肝血,殊不知肝血张常宁解锁新身份!晒与老公海边甜蜜合影,海边豪华别墅曝光众所周知,新一届中国女排正在远征土耳其,世界女排联赛第一阶段已经结束,中国女排首场比赛以三胜一负开局。目前中国女排排名第二,日本女排排名第一。前段时间被退役传闻困扰的张常宁,以另一赵薇人间蒸发九个月后现身,晒出父亲的黑白照片,称自己一无所有去年8月,知名演员赵薇突然遭到封杀,整个人在互联网上被除名,同时她曾经参演的作品像还珠格格的演职员表当中同样搜不到她的名字,网上的传闻也是各种各样的消息满天飞,始终没有一个确切的答夏日微醺,草木欣然,在辰山植物园感受初夏请大家注意个人防护,不扎堆不聚集,做到防疫三件套,防护五还要。阔别两月分外想念ShanghaiChenshanBotanicalGarden辰山植物园已恢复开放了是不是有许多的小伙美好的初夏时光,辰山的花儿开成海请大家注意个人防护,不扎堆不聚集,做到防疫三件套,防护五还要。静待许久的辰山园中一派郁郁葱葱山风拂过,带着缕缕花香鼠尾草绣球月季各种各样的花草装点着初夏的梦辰山下,花尚在夏日倾情,2022朋友圈晒娃的文案抖音上最火的萌宝文案一,2022朋友圈晒娃的文案1。我的孩子,不要胆怯,不要回头,勇敢的迈开脚步向前走吧。2。我的岁月因你而暖因你而乐3。宝贝,你就是我的掌上宝4。不管发生什么,我都会和你在一起,我永奇葩!浓眉豪宅与朋友聚会炫耀4月5日常规赛打完我就没投过篮了6月12日消息,日前,湖人当家球星安东尼戴维斯正在休赛期享受人生,他在与朋友聚会中透露,自己自从4月5日常规赛结束以来就没投过篮,令人惊讶!本赛季常规赛,浓眉因为伤病频仍仅代表湖人适合发朋友圈的文字,总有一个打动你1。今天应做的事没有做,明天再早也是耽误了。2。世界这么大,人生这么长,总会有这么一个人,让你想要温柔地对待。3。想见你,在吞云吐月的疾风后,在日复一日的寻常里。4。窗外的梧桐叶随