空抽象类是一种不好的做法吗?为什么?

发布于 2024-08-11 06:18:26 字数 321 浏览 4 评论 0原文

我们的代码库中有几个空的抽象类。我觉得那很丑。但除了这个非常愚蠢的原因(丑陋)之外,我应该重构它(例如空接口)吗?

否则,代码是健壮的并且经过良好测试。因此,如果只是出于“美观”的原因,我会忽略并保留空的抽象类。

你怎么认为?

编辑:

1)通过“空抽象类”,我的意思是:

public abstract class EmptyAbstractClass {}

2)“空”的原因:Hibernate。我根本不掌握这个持久化框架。我只知道接口不能映射到表,并且由于这个技术原因,类比接口更受青睐。

We have several empty abstract class in our codebase. I find that ugly. But besides this very stupid reason (ugliness), should I refactor it (into empty interface e.g.) ?

Otherwise, the code is robust and well tested. So if it's only for a "aesthetic" reason, I will pass and let the empty abstract classes remain.

What do you think?

EDIT :

1) By "empty abstract class", I mean something like :

public abstract class EmptyAbstractClass {}

2) The reason for the "emptiness" : Hibernate. I don't master this persistence framework at all. I just understand that an interface cannot be mapped to a table, and for this technical reason, a class has been preferred to an interface.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

会傲 2024-08-18 06:18:26

在这种情况下,接口是更可取的,因为它使您的代码更易于维护。也就是说,您只能扩展一个类,但可以实现许多接口。

如果现在绝对没有直接的影响我就不会去碰它。如果出现维护事件需要您进行更改,那么我会重构它,因为我已经在代码中了。

换句话说,如果它没有坏,就不要修理它。

Interfaces are preferable in this case because it makes your code more robust for maintenance. That is, you can only extend a single class but you may implement many interfaces.

If there is absolutely no direct effect right now I would not touch it. If the maintenance event turns up that requires you to make a change then I would refactor it since I am already in the code.

In other words if it ain't broke don't fix it.

晨光如昨 2024-08-18 06:18:26

在我看来,这是创建对象层次结构的结果,该对象层次结构最终在其最顶层没有任何通用功能。我怀疑直接子类本身是抽象的,或者至少有自己的子类。另一种可能性是您的代码中散布着许多 instanceof 函数。

空的最顶层本身并不是什么大问题,但我会检查以验证实际上不存在通用功能。假设它确实存在,我会考虑在父类中提取通用功能。我肯定会留意 instanceof 并认真考虑重构它。有关示例,请参阅重构模式 (Kerievsky)。

Sounds to me like this is the result of creating an object hierarchy that ended up not having any common functionality at it's top most levels. I suspect that the immediate subclasses are abstract themselves or at least have subclasses of their own. The other likelihood is that your code has a lot of instanceof functions scattered throughout it.

The empty topmost level isn't a huge deal in and of itself but I would check to verify that no common functionality actually exists. Assuming it does exist I would look at pulling the common features up in the parent classes. I would definitely keep a look out for instanceof and think seriously about refactoring it. Refer to Refactoring to Patterns (Kerievsky) for examples.

小嗲 2024-08-18 06:18:26

要问的问题是:“我想用这段代码做什么,因为这些空的抽象类在那里,而我无法做,或者很难做?”如果答案是“没什么”,你就应该不理会他们。如果答案是“某事”,那么删除它们可能是值得的 - 但如果可以的话,请先与创建它们的人交谈,以确保它们没有任何微妙的目的。例如,也许您的代码使用反射来查找特定 ABC 的所有实例并对它们执行特殊操作,在这种情况下,您的重构可能会以微妙的方式破坏代码。

The question to ask is: "What do I want to do with this code that I can't do, or find hard to do, because these empty abstract classes are there?" If the answer is 'nothing', you should leave them alone. If the answer is "something", it may well be worthwhile to remove them - but if you can, speak to the people who created them first, just to make sure there isn't some subtle purpose to them. For example, perhaps your code uses reflection to find all instances of a particular ABC and do special things to them, in which case your refactoring could break the code in subtle ways.

别忘他 2024-08-18 06:18:26

它不一定比替代方案更难看,后者可能是重复的代码。

在理想的世界中,您将能够仅使用接口进行建模。例如:
车辆 ->汽车->庞蒂亚克。

但所有车辆的逻辑可能都是相同的,因此接口是不合适的。而且你没有特定于汽车的逻辑。但您确实需要汽车抽象,因为您的 TrafficLightController 想要区分汽车和自行车。

在这种情况下,您需要制作并抽象 Car。

或者,您可以创建一个接口Vehicle,一个VehicleImpl实现Vehicle,一个接口Car扩展Vehicle,一个接口Pontiac实现Car,一个PontiacImpl实现Pontiac扩展VehicleImpl。我个人不喜欢接口的并行层次结构,因为它比空抽象类更能防止空抽象类。

所以我想这是一个品味问题。

一个警告;如果您使用大量代理类(例如 Spring 和一些测试框架),并行接口层次结构很可能会减少意外错误。

It is not necessarily more ugly than the alternative, which may be repeating code.

In an ideal world you would be able to model using only interfaces. for example:
Vehicel -> Car -> Pontiac.

But there may be logic which is the same for all Vehicels, so an interface is not appropriate. And you don't have logic specific to Cars. But you do want a Car abstraction because your TrafficLightController wants to distinguish between Cars and Bicycles.

In that case you need to make and abstract Car.

Or you can make an interface Vehicle, a VehicleImpl implements Vehicle, an interface Car extends Vehicle, an interface Pontiac implements Car, and a PontiacImpl implements Pontiac extends VehicleImpl. I personally dislike a parallel hierarchy of interfaces for the sake of preventing an empty abstract class more than an empty abstract class.

So I guess it's a matter of taste.

One caveat; if you use a lot of proxied classes like with Spring and some testing frameworks a parallel interface hierarchy might well result in less unexpected errors.

爱冒险 2024-08-18 06:18:26

空的抽象类对我来说没有任何意义,抽象类应该用于继承某些行为。所以,我倾向于同意,这是一个相当丑陋的设计,并且对抽象类的使用非常糟糕,应该首选标记接口。所以你有两个选择:

  1. 等到你需要扩展另一个类来满足真正的需要,然后对抽象类感到恼火,然后用接口替换它。
  2. 不要等待并修复设计。

在这种特殊情况下,确实,目前的实际设计并没有真正造成伤害,因此您可以接受它。但是,我认为替换这些抽象类是一个非常简单的重构(使它们成为接口并用 implements 替换 extends ,在这种情况下会出现编译错误),我真的看不出什么可能会坏,所以我会去尝试。

就我个人而言,人们当然可能不同意,我不喜欢过于防守。有了像如果它没有坏,就不要修复它这样的规则,你永远不会重构任何东西(“我的测试通过了,我为什么要重构?”)。冻结代码绝对不是正确的解决方案,积极测试才是正确的解决方案

Empty abstract classes don't make any sense to me, abstract classes should be used to inherit some behavior. So, I tend to agree, it's a pretty ugly design and a very bad use of abstract classes, marker interfaces should be preferred. So you have two options:

  1. Wait till you need to extend another class for a real need and get annoyed by the abstract class to replace it by an interface.
  2. Don't wait and fix the design.

In this particular situation, it is true that the actual design doesn't really hurt, for now, so you can live with it. However, I think replacing these abstract classes is a pretty easy refactoring (make them interfaces and replaces extends with implements where you get compilation errors) and I really can't see what could get broken so I would go for it.

Personally, and people may of course disagree, I don't like being too defensive. With rules like if it's ain't broke, don't fix it, you'll never refactor anything ("my tests passes, why should I refactor?"). Freezing the code is definitely not the right solution, testing aggressively is the right solution.

飞烟轻若梦 2024-08-18 06:18:26

问自己这个问题:如果由于重构而导致生产中出现某些问题,而您的继续工作取决于您花时间修复实际上没有损坏的问题的决定的合理性,您会怎么说?

“它很丑陋,而且在审美上对我来说很冒犯”,这并不是我愿意拿自己的工作作为赌注的答案。

在这个阶段,我建议谨慎行事,与丑陋者共处。

Ask yourself this question: If, as a result of your refactoring, something breaks in production and your continued employment depends on how well you justify your decision to spend time fixing something that wasn't actually broken, what do you say?

"It was ugly and aesthetically offensive to me" isn't an answer I'd like to stake my job on.

At this stage, I say play it safe and live with the Ugly.

掩于岁月 2024-08-18 06:18:26

如果您有以下模式,您会发现它是最灵活的:

interface Fooable
{
   void foo();
   void bar();
}

abstract class AbstractFoo
    implements Fooable
{
}

class Foo
    extends AbstractFoo
{
   public void foo()
   {
   }

   public void bar()
   {
   }
}

这种方式您始终可以通过接口,但如果您后来发现您有通用的代码,您可以将其放在抽象类中,而不必制作所有类别的更改。

除非你有充分的理由不采用这种模式(我怀疑你在这种情况下没有这样做),否则我建议使用它。这可能会导致空抽象类,但我认为这是可以的(有点奇怪,但还可以)。

如果接口中确实没有方法或只有一个方法,那么我会跳过抽象类。

从编译器/功能的角度来看,接口和抽象类之间没有真正的区别,其中所有方法都是抽象的。接口将比抽象类更灵活,并且接口+抽象类将是最灵活的。

如果这是我的代码,我会将它们更改为接口......

If you have the following pattern you will find it to be the most flexible:

interface Fooable
{
   void foo();
   void bar();
}

abstract class AbstractFoo
    implements Fooable
{
}

class Foo
    extends AbstractFoo
{
   public void foo()
   {
   }

   public void bar()
   {
   }
}

This way you can always pass by the interface but if you later find that you have code that can be common you can put it in the abstract class without having to make changes to all of the classes.

Unless you have a good reason for not doing that pattern (and I suspect you don't in this case) I would suggest using it. This can wind up with empty abstract classes but I think it is ok (a little odd, but ok).

If there truly are no methods in the interface or only one then I would skip the abstract class.

From a compiler/functional point of view there is no real difference between an interface and an abstract class where all method are abstract. The interface will be more flexible than the abstract class, and the interface + abstract class will be the most flexible of all.

If it were my code I'd make the change to them being interfaces...

时光无声 2024-08-18 06:18:26

关键是您只能从一个抽象类进行扩展,同时可以实现更多接口。

显然,“空抽象类”设计决策是为了防止实现类从其他类扩展。

如果是我,我会放手,否则它可能会破裂。最好的方法仍然是联系原始开发人员并询问推理(并将它们添加为抽象类中的注释,以方便您和将来的使用)。

The key is that you can extend from only one abstract class, while you can implement more interfaces.

Apparently the "empty abstract class" design desicion was made so that it prevents the implementing class from extending from another classes.

If it was me I'd let it go, otherwise it might break. Best is still to contact the original developers and ask for reasoning (and add them as comments in the abstract class for your and future convenience).

暮倦 2024-08-18 06:18:26

根据面向对象编程理论,继承的主要目的是多态性、代码重用和封装。一个空的抽象类(当我说这个时,我的意思是真正空的,没有构造函数,没有方法,没有属性。什么都没有!)并不能实现这种编程技术所希望的三个目标中的任何一个。它相当于
if(true){...}。将其更改为接口并不会真正使其变得更好。

如果你想重构代码,我建议你以与你正在思考的方向相反的方向思考,我的意思是:尝试从共享抽象父类的所有类中抽象属性、方法和构造函数。

这是一项艰苦的工作,短期内回报很少,但它极大地提高了代码的可维护性,因为核心逻辑更改只需要做一次。我怀疑使用这些空抽象类的原因是为了识别一组必须共享共同点的类,否则 Object 和抽象类之间有什么区别

According to object oriented programming theory the main purpose for inheritance is polymorphism, code reuse and encapsulation. An empty abstract class (and when i say this i mean truly empty, no constructors, no methods, no properties. nothing!) does not achieve any of the three goals hoped by this programming technique. It is the equivalent to
if(true){...}. changing it to an interface does not really makes it any better.

If you want to refactor the code i would advise you to think in the direction opposite to the one you are thinking, what i mean by this is: try to abstract properties, methods and constructors from all classes that share a abstract parent class.

This is hard work with little reward in the short term but it increases the maintainability of the code dramatically since a core logic change would have to be done only once. I suspect the reason for using those empty abstract classes is to identify a group of classes that must share something in common otherwise what would be the difference between Object and the abstract class

如此安好 2024-08-18 06:18:26

明显的问题是,为什么把它放在那里?以及它是如何使用的?

我的猜测是,要么 (a) 这是一些错误开始的痕迹。早期的草案在抽象类中包含了一些数据或函数,但是随着程序员的工作,这些数据或函数都被移到了其他地方,直到只剩下一个空壳。但更有可能的是(b)在某些时候有代码说“if(x instanceof ...”。我认为这是糟糕的设计,基本上使用类成员资格作为标志。如果您需要一个标志,请创建一个标志,不要通过创建类或接口然后将其用作标志来假装您“更加面向对象”,这可能符合一些更好编码的教科书定义,但实际上只会使代码更加混乱

+1 。 Monachus 指出空接口并不比空抽象类好,是的,是的,我知道 Sun 自己用 Cloneable 做到了这一点,但我认为这是一个蹩脚的解决方案。

The obvious questions are, Why was it put there? and How is it used?

My guess is that, either (a) This is the vestige of some false start. An early draft had some data or functions in the abstract class, but as the programmer worked these were all moved elsewhere, until there was nothing left but an empty shell. But more likely (b) At some point there is code that says "if (x instanceof ...". I think this is bad design, basically using a class membership as a flag. If you need a flag, create a flag, don't pretend you're being "more object-oriented" by creating a class or an interface and then using it as a flag. That may fit some textbook definition of better coding but in reality just makes code more confusing.

+1 to Monachus for pointing out that an empty interface is no better than an empty abstract class. Yes, yes, I know Sun did that themselves with Cloneable, but I think that was a lame solution to the problem.

静赏你的温柔 2024-08-18 06:18:26

虽然我不知道空类映射到的表中会有什么内容,但如果该类有某种用途,那么就保留它,直到您有机会重构一些。

我肯定会做的是:写一条关于为什么这个类存在于文件中的评论。干净且“美丽”的代码的一大原因是不让其他开发人员思考。注释可以帮助解决这个问题,即使代码并不像它应有的那么漂亮。

While I don't get what will be in the table that the empty class is mapped to, if the class serves some purpose, well, keep it, until you have an opportunity to refactor some.

What I would definitely do is: write a comment about why this class exists in the file. One big reason for clean and "beautiful" code is to not make other developers think. A comment can help with that, even if the code is not as pretty as it could be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文