美文网首页Java 杂谈Java总社区
开发中滥用面向对象,你是否违背了编程原则

开发中滥用面向对象,你是否违背了编程原则

作者: java劝退师图图 | 来源:发表于2019-05-09 14:10 被阅读15次

    Switch 声明

    Switch 声明(Switch Statements)

    你有一个复杂的switch语句或if序列语句。

    问题原因

    面向对象程序的一个最明显特征就是:少用switch和case语句。从本质上说,switch语句的问题在于重复(if序列也同样如此)。你常会发现switch语句散布于不同地点。如果要为它添加一个新的case子句,就必须找到所有switch语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。

    大多数时候,一看到switch语句,就应该考虑以多态来替换它。

    解决方法

    问题是多态该出现在哪?switch 语句常常根据类型码进行选择,你要的是“与该类型码相关的函数或类”,所以应该运用提炼函数(Extract Method)将switch语句提炼到一个独立函数中,再以搬移函数(Move Method)将它搬移到需要多态性的那个类里。

    如果你的switch是基于类型码来识别分支,这时可以运用以子类取代类型码(Replace Type Code with Subclass)或以状态/策略模式取代类型码(Replace Type Code with State/Strategy)。

    一旦完成这样的继承结构后,就可以运用以多态取代条件表达式(Replace Conditional with Polymorphism)了。

    如果条件分支并不多并且它们使用不同参数调用相同的函数,多态就没必要了。在这种情况下,你可以运用以明确函数取代参数(Replace Parameter with Explicit Methods)。

    如果你的选择条件之一是 null,可以运用引入 Null 对象(Introduce Null Object)。

    收益

    提升代码组织性。

    何时忽略

    如果一个switch操作只是执行简单的行为,就没有重构的必要了。

    switch常被工厂设计模式族(工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory))所使用,这种情况下也没必要重构。

    重构方法说明

    提炼函数(Extract Method)

    问题

    你有一段代码可以组织在一起。

    void printOwing() {

      printBanner();

      //print details

      System.out.println("name: " + name);

      System.out.println("amount: " + getOutstanding());

    }

    解决

    移动这段代码到一个新的函数中,使用函数的调用来替代老代码。

    void printOwing() {

      printBanner();

      printDetails(getOutstanding());

    }

    void printDetails(double outstanding) {

      System.out.println("name: " + name);

      System.out.println("amount: " + outstanding);

    }

    搬移函数(Move Method)

    问题

    你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

    解决

    在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。

    以子类取代类型码(Replace Type Code with Subclass)

    问题

    你有一个不可变的类型码,它会影响类的行为。

    解决

    以子类取代这个类型码。

    以状态/策略模式取代类型码(Replace Type Code with State/Strategy)

    问题

    你有一个类型码,它会影响类的行为,但你无法通过继承消除它。

    解决

    以状态对象取代类型码。

    以多态取代条件表达式(Replace Conditional with Polymorphism)

    问题

    你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。

    class Bird {

      //...

      double getSpeed() {

        switch (type) {

          case EUROPEAN:

            return getBaseSpeed();

          case AFRICAN:

            return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;

          case NORWEGIAN_BLUE:

            return (isNailed) ? 0 : getBaseSpeed(voltage);

        }

        throw new RuntimeException("Should be unreachable");

      }

    }

    解决

    将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

    abstract class Bird {

      //...

      abstract double getSpeed();

    }

    class European extends Bird {

      double getSpeed() {

        return getBaseSpeed();

      }

    }

    class African extends Bird {

      double getSpeed() {

        return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;

      }

    }

    class NorwegianBlue extends Bird {

      double getSpeed() {

        return (isNailed) ? 0 : getBaseSpeed(voltage);

      }

    }

    // Somewhere in client code

    speed = bird.getSpeed();

    以明确函数取代参数(Replace Parameter with Explicit Methods)

    问题

    你有一个函数,其中完全取决于参数值而采取不同的行为。

    void setValue(String name, int value) {

      if (name.equals("height")) {

        height = value;

        return;

      }

      if (name.equals("width")) {

        width = value;

        return;

      }

      Assert.shouldNeverReachHere();

    }

    解决

    针对该参数的每一个可能值,建立一个独立函数。

    void setHeight(int arg) {

      height = arg;

    }

    void setWidth(int arg) {

      width = arg;

    }

    引入 Null 对象(Introduce Null Object)

    问题

    你需要再三检查某对象是否为 null。

    if (customer == null) {

      plan = BillingPlan.basic();

    }

    else {

      plan = customer.getPlan();

    }

    解决

    将 null 值替换为 null 对象。

    class NullCustomer extends Customer {

      Plan getPlan() {

        return new NullPlan();

      }

      // Some other NULL functionality.

    }

    // Replace null values with Null-object.

    customer = (order.customer != null) ? order.customer : new NullCustomer();

    // Use Null-object as if it's normal subclass.

    plan = customer.getPlan();

    临时字段

    临时字段(Temporary Field)的值只在特定环境下有意义,离开这个环境,它们就什么也不是了。

    问题原因

    有时你会看到这样的对象:其内某个实例变量仅为某种特定情况而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初设置目的,会让你发疯。 通常,临时字段是在某一算法需要大量输入时而创建。因此,为了避免函数有过多参数,程序员决定在类中创建这些数据的临时字段。这些临时字段仅仅在算法中使用,其他时候却毫无用处。 这种代码不好理解。你期望查看对象字段的数据,但是出于某种原因,它们总是为空。

    解决方法

    可以通过提炼类(Extract Class)将临时字段和操作它们的所有代码提炼到一个单独的类中。此外,你可以运用以函数对象取代函数(Replace Method with Method Object)来实现同样的目的。

    引入 Null 对象(Introduce Null Object)在“变量不合法”的情况下创建一个 null 对象,从而避免写出条件表达式。

    收益

    更好的代码清晰度和组织性。

    重构方法说明

    提炼类(Extract Class)

    问题

    某个类做了不止一件事。

    解决

    建立一个新类,将相关的字段和函数从旧类搬移到新类。

    以函数对象取代函数(Replace Method with Method Object)

    问题

    你有一个过长函数,它的局部变量交织在一起,以致于你无法应用提炼函数(Extract Method) 。

    class Order {

      //...

      public double price() {

        double primaryBasePrice;

        double secondaryBasePrice;

        double tertiaryBasePrice;

        // long computation.

        //...

      }

    }

    解决

    将函数移到一个独立的类中,使得局部变量成了这个类的字段。然后,你可以将函数分割成这个类中的多个函数。

    class Order {

      //...

      public double price() {

        return new PriceCalculator(this).compute();

      }

    }

    class PriceCalculator {

      private double primaryBasePrice;

      private double secondaryBasePrice;

      private double tertiaryBasePrice;

      public PriceCalculator(Order order) {

        // copy relevant information from order object.

        //...

      }

      public double compute() {

        // long computation.

        //...

      }

    }

    引入 Null 对象(Introduce Null Object)

    问题

    你需要再三检查某对象是否为 null。

    if (customer == null) {

      plan = BillingPlan.basic();

    }

    else {

      plan = customer.getPlan();

    }

    解决

    将 null 值替换为 null 对象。

    class NullCustomer extends Customer {

      Plan getPlan() {

        return new NullPlan();

      }

      // Some other NULL functionality.

    }

    // Replace null values with Null-object.

    customer = (order.customer != null) ? order.customer : new NullCustomer();

    // Use Null-object as if it's normal subclass.

    plan = customer.getPlan();

    异曲同工的类

    异曲同工的类(Alternative Classes with Different Interfaces)

    两个类中有着不同的函数,却在做着同一件事。

    问题原因

    这种情况往往是因为:创建这个类的程序员并不知道已经有实现这个功能的类存在了。

    解决方法

    如果两个函数做同一件事,却有着不同的签名,请运用函数改名(Rename Method)根据它们的用途重新命名。

    运用搬移函数(Move Method)、添加参数(Add Parameter)和令函数携带参数(Parameterize Method)来使得方法的名称和实现一致。

    如果两个类仅有部分功能是重复的,尝试运用提炼超类(Extract Superclass)。这种情况下,已存在的类就成了超类。

    当最终选择并运用某种方法来重构后,也许你就能删除其中一个类了。

    收益

    消除了不必要的重复代码,为代码瘦身了。

    代码更易读(不再需要猜测为什么要有两个功能相同的类)。

    何时忽略

    有时合并类是不可能的,或者是如此困难以至于没有意义。例如:两个功能相似的类存在于不同的 lib 库中。

    重构方法说明

    函数改名(Rename Method)

    问题

    函数的名称未能恰当的揭示函数的用途。

    class Person {

      public String getsnm();

    }

    解决

    修改函数名。

    class Person {

      public String getSecondName();

    }

    搬移函数(Move Method)

    问题

    你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

    解决

    在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。

    添加参数(Add Parameter)

    问题某个函数需要从调用端得到更多信息。

    class Customer {

      public Contact getContact();

    }

    解决为此函数添加一个对象函数,让改对象带进函数所需信息。

    classCustomer{publicContactgetContact(Date date);}

    令函数携带参数(Parameterize Method)

    问题

    若干函数做了类似的工作,但在函数本体中却包含了不同的值。

    解决

    建立单一函数,以参数表达哪些不同的值。

    提炼超类(Extract Superclass)

    问题

    两个类有相似特性。

    解决

    为这两个类建立一个超类,将相同特性移至超类。

    被拒绝的馈赠

    被拒绝的馈赠(Refused Bequest)

    子类仅仅使用父类中的部分方法和属性。其他来自父类的馈赠成为了累赘。

    问题原因

    有些人仅仅是想重用超类中的部分代码而创建了子类。但实际上超类和子类完全不同。

    解决方法

    如果继承没有意义并且子类和父类之间确实没有共同点,可以运用以委托取代继承(Replace Inheritance with Delegation)消除继承。

    如果继承是适当的,则去除子类中不需要的字段和方法。运用提炼超类(Extract Superclass)将所有超类中对于子类有用的字段和函数提取出来,置入一个新的超类中,然后让两个类都继承自它。

    收益

    提高代码的清晰度和组织性。

    重构方法说明

    以委托取代继承(Replace Inheritance with Delegation)

    问题

    某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

    解决

    在子类中新建一个字段用以保存超类;

    调整子类函数,令它改而委托超类;

    然后去掉两者之间的继承关系。

    提炼超类(Extract Superclass)

    问题

    两个类有相似特性。

    解决

    为这两个类建立一个超类,将相同特性移至超类。

    相关文章

      网友评论

        本文标题:开发中滥用面向对象,你是否违背了编程原则

        本文链接:https://www.haomeiwen.com/subject/ciswoqtx.html