在将大型类重构为较小的类之前,您是否查看了方法使用的变量? [关闭]

问题描述 投票:3回答:7

我有兴趣探索这样一种观点,即方法与他们使用的成员varialbes之间的关系可以暗示如何将类分解为更小的部分。这个想法是一组变量与一个责任密切相关,并且应该根据SRP包含在一个类中。

对于一个简单的例子,一个类如:

public class Rectangle {
  private int width; 
  private int height;
  private int red,green,blue;
  public int area() { return width * height; }
//just an example, didn't check the api.
  public Color color () { return new Color (red, green, blue); } 
}

应该重构为:

public class Rectangle {
  private Dimension size;
  private Color color;
  ...
}

因为分解将是:

面积:宽度,高度颜色:红色,绿色,蓝色

由于这些变量在相同的方法中使用,因此它们明显相关,并且可以制成它自己的类。我知道这个例子可能太琐碎了,但请耐心等待,并尝试在这里思考更大。如果其他方法也使用这些变量,则它们很可能也相关,也可以移动到新类中。

只是为了好玩,我创建了一个尝试这样做的小plugin for Eclipse。它将对方法 - >变量,变量 - >方法进行细分,并尝试将方法组合在一起,然后直接或间接使用这些变量。

这是你在编码时做的事情,它实际上有用吗?

java eclipse-plugin refactoring
7个回答
2
投票

我不能说这曾经是我在编码时想到的东西。是的,它可能会带来一些改进,但我认为你在实践中发现自己要做的就是创建大量的类,这些类更难以处理。

不过,作为“代码味道”,它可能有一些优点。如果你看到一个包含大量成员变量的类,并且大多数只使用少数方法,那通常是一个不好的标志。


2
投票

我可以想象这是一种合理的代码味道。在我们的例子中,简单的setter / getter并不太有说服力。例如,重构只会产生一个带有a和b的更复杂的对象:

public class A {
  private String a1;
  public void aMeth () {
     print(a1);
  }
}
public class B {
  private String b1;
  public void bMeth () {
     print(b1);
  }
}

public class A { 
  private A a;
  private B b;
  ... insert infinite regress 
}

但是,我可以看到这是一个指南,你有一组变量纠缠在大怪物类中,其中变量分组对应于概念分解。你在想更多这样的事吗?

public class Rectangle {
  private int width; 
  private int height;
  private int red,green,blue;
  public int area() { return width * height; }
//just an example, didn't check the api.
  public Color color () { return new Color (red, green, blue); } 
}

矩形应具有尺寸和颜色。在我看来,如果你能清楚地发现只有彼此纠缠的私有变量集,这是一个有用的东西,只要你省略非纠缠变量(即如果你把变量看作图顶点,我想你想要检测连接的子图但省略大小为1的子图


1
投票

我认为每个类应该按照它的作用来组织,而不是它将使用什么变量。我建议让每个班级都有一个目的(单一责任原则)。而不是根据他们对变量的使用来分解,根据他们的责任或目的来分离类。

在我看来,编写干净的代码不仅仅是代码看起来有多好,而且还有多容易阅读。代码应按逻辑分组并具有一致的样式。请记住,某些类不会使用所有变量。在java中你有mutator和accessor方法。这些方法用于获取或设置单个变量。

例:

public class Rectangle {

    private int lenght = 0;
    private int width = 0;

    public Rectangle (int length, width)
        {
        this.length = length;
        this.width = width;
    }

    public int getLength(int length)
    {
        return length;
    }

    public int getWidth(int width)
    {
        return wdith;
    }


    public void setLength(int length)
    {
        this.length = length;
    }

    public void setWidth(int width)
    {
        this.width = width;
    }
}

我不会将此类拆分为RectangleLength和RectangleWidth类。这些方法只承担单一责任,即表示矩形。如果此类包含数学函数,您可能希望将其分解为RectangleGeometry类。


1
投票

我做到了这一点。甚至创建了一个表,第一列中的方法,顶部的实例变量,以及指示哪个使用哪个。然后排序 - 有点像热图这样排序,但更随意,更手动 - 看看哪些变量,以及哪些方法一起跟踪。它有效并且很有用。但它深深扎根于我的诡计之中;我很少使用它。我必须看看你的插件;如果这样做的工作较少,我可能会做得更多。


1
投票

这当然是一种很好的重构方法。在Working Effectively With Legacy Code中,Michael Feathers在一个名为Seeing Responsibilities的部分中描述了一种技术:

启发式#4寻找内部关系

寻找实例变量和方法之间的关系。某些方法使用某些实例变量而不是其他方法吗?

然后,他继续描述一种方法,您可以在其中绘制一个称为特征草图的图表,该图表显示了类和内部方法调用的方法。从图中,您可以轻松地直观地识别互连方法调用的集群,这些调用是重构为新的,更具凝聚力的类的候选者。

如果您的Eclipse插件可以快速生成图书中描述的图形,那将是非常酷的 - 我可以看到它是一个有用的重构工具。


0
投票

听起来像有人读到有关Law of Demeter的内容,并试图将其置于极端。我不得不说,我总是以一定的谨慎态度对待“脱钩”狂热者,尤其是因为有些人确实提倡设计中的“零耦合”,而忽略了这样一个事实,即没有任何耦合,系统就没用了。

尽管如此,减少代码中的依赖性使得测试更容易,并且对其他重构更具弹性,而且我发现它往往会导致更简单,更明显的代码。健康的平衡是努力的目标。


-1
投票

总体而言,这似乎是一个坏主意。

首先,类表示相关数据和/或方法的集合。它根本不需要任何方法来保持有用的类。为了获得良好的封装,您可能需要吸气剂和固定剂;根据定义,这些方法不会访问多个变量。这并不意味着这些变量是否属于同一个类。

最后,一个方法属于该类,如果它提供了一种操作类,计算自身的一些信息,构造类等的方法;方法可能属于某个类但不能访问大多数成员变量的原因有很多。然后是静态方法;根据定义,这些应该在其他类别吗?

如果一个类中包含太多概念,则应该将其拆分。但这些概念只是松散地映射到方法和变量。计算变量使用是一种可靠的方法,可以使您的类层次结构过于复杂,而且没有任何好处。

© www.soinside.com 2019 - 2024. All rights reserved.