寻求重新设计界面的建议

发布于 2024-09-04 06:15:33 字数 652 浏览 3 评论 0原文

作为维护大量遗留代码的一部分,我们需要更改部分设计,主要是为了使其更具可测试性(单元测试)。我们需要解决的问题之一是组件之间现有的接口。两个组件之间的接口是一个仅包含静态方法的类。

简化示例:

class ABInterface {

    static methodA();
    static methodB();
    ...
    static methodZ();
};

组件 A 使用该接口,以便不同的方法可以使用 ABInterface::methodA() 来准备一些输入数据,然后调用组件 B 中的相应函数。

现在,出于各种原因,我们尝试重新设计该接口:

  • 扩展我们的单元测试覆盖范围 - 我们需要解决组件和存根/模拟之间的依赖关系

  • 这些组件之间的接口偏离了原始设计(即,许多用于组件间 i/f 的新功能是在该接口类之外创建的)。

  • 代码很旧,随着时间的推移发生了很大的变化,需要重构。

更改不应对系统的其余部分造成破坏。我们尝试限制在生产代码中留下许多测试所需的工件。性能非常重要,重新设计后应该不会(或非常少)降低性能。代码是 C++ 中的 OO。

我正在寻找一些想法,采取什么方法。关于如何有效地做到这一点有什么建议吗?

As a part of maintaining large piece of legacy code, we need to change part of the design mainly to make it more testable (unit testing). One of the issues we need to resolve is the existing interface between components. The interface between two components is a class that contains static methods only.

Simplified example:

class ABInterface {

    static methodA();
    static methodB();
    ...
    static methodZ();
};

The interface is used by component A so that different methods can use ABInterface::methodA() in order to prepare some input data and then invoke appropriate functions within component B.

Now we are trying to redesign this interface for various reasons:

  • Extending our unit test coverage - we need to resolve this dependency between the components and stubs/mocks are to be introduced

  • The interface between these components diverged from the original design (ie. a lots of newer functions, used for the inter-component i/f are created outside this interface class).

  • The code is old, changed a lot over the time and needs to be refactored.

The change should not be disruptive for the rest of the system. We try to limit leaving many test-required artifacts in the production code. Performance is very important and should be no (or very minimal) degradation after the redesign. Code is OO in C++.

I am looking for some ideas what approach to take. Any suggestions on how to do this efficiently?

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

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

发布评论

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

评论(3

幸福丶如此 2024-09-11 06:15:33

最简单的答案是将具有静态接口的旧库包装在外观中,然后重构代码以调用新的外观而不是旧库。这个新的楔子应该允许替换库以进行单元测试。首先在单个方法上进行测试,看看如何实现它。

真正困扰我的是当一个问题被“性能问题”污染时。我们都编写过对性能至关重要的代码,并且没有人故意建议编写性能较差的代码。我发现这些“担忧”通常来自那些谴责每一项变革的反对者,并且真的不知道为什么某个变革会或不会表现良好。请记住,唯一有效的性能证明来自测试。运行性能测试并建立基线。做出你的改变。在新代码中再次运行性能测试。展示实际影响。只有这样您才能对变更的实际影响做出决定。您绝不应该让对周期的争论主导您的设计,除非另有证明。

听起来你的项目中的某个重要人物是一位老 C 程序员*,他很久以前就听到一些大声说“让 C++ 运行得更快的唯一方法是使用静态方法”之类的话。问题是他还相信。 20 年前,大声喧哗的人可能是对的,但编译器和优化器在这 2 年来已经有了巨大的进步。所以尝试一下你的改变吧。

如果您使用现代编译器,优化器很可能会删除目标代码中额外的取消引用,这意味着您根本不会添加任何运行时影响。

如果性能至关重要,但您没有进行性能测试,或者您没有使用现代编译器,或者您没有调整所有发布构建优化(例如,使用配置文件引导优化)那么在担心额外抽象层的性能之前,您需要解决更大的工程问题。

  • 注:我也是一个老C程序员,20年前也曾说过这样的蠢话。不同之处在于,我了解到某些优化比其他优化要重要得多,而且新编译器非常擅长自行解决大多数问题。我过早地“优化”事物的尝试通常会导致维护成本高昂的代码,而这些代码通常不会优于现有的编译器设置。

The simplest answer is to wrap the old library that has the static interface in a facade, then refactor the code to call the new facade instead of the old library. This new wedge should permit substitution of the library for unit testing purposes. Test it out on a single method first, just to see how to implement it.

What really bothers me is when a question is tainted with "performance concerns". We've all written performance critical code, and nobody ever deliberately suggests writing poorly performant code. I find these "concerns" usually come from the naysayers who decry every change, and really don't have a clue as to why a certain change would or wouldn't perform well. Remember, the only valid proof of performance comes from testing. Run your performance test and establish a baseline. Make your changes. Run your performance tests again in the new code. Demonstrate the actual impact. Only then can you make a decision as to the actual impact of a change. You should never allow quibbles over cycles to dominate your designs until demonstrated otherwise.

It sounds like someone important on your project is an old C programmer* who long ago heard some loudmouth say something like "the only way to make C++ run fast is to use static methods." The problem is that he still believes it. 20 years ago the loudmouth may have been right, but compilers and optimizers have vastly improved over those 2 decades. So give your change a try.

The chances are good that if you're using a modern compiler the optimizer is going to remove the extra dereferences in the object code anyway, meaning you'll have added no run-time impact at all.

And if performance is all-critical yet you don't have performance tests, or if you're not using a modern compiler, or if you don't have all the release-build optimizations tweaked (using Profile Guided Optimization, for example) then you have much bigger engineering problems that you need to take care of long before worrying about the performance of an extra layer of abstraction.

  • Note: I am also an old C programmer who also used to say stupid things like that 20 years ago. The difference is that I have learned that some optimizations are far more critical than others, and that the new compilers are amazingly good at figuring most stuff out on their own. My attempts to "optimize" things prematurely usually ended in expensive-to-maintain code that usually doesn't outperform the stock compiler settings anyway.
一页 2024-09-11 06:15:33

如果方法 A-Z 是非静态且虚拟的,那么您可以轻松完成此操作,对吗?因此,如果静态方法 A-Z 调用非静态虚拟方法 A-Z,您将能够覆盖行为。知道这一点后,您需要一种方法来更改包含非静态版本的实例以进行测试。

或者,您可以采用要重构的两个类,并让它们使用这个纯静态类的包装器。然后两者可以根据需要进行不同程度的分歧。

这就是我所有的想法,没有考虑真正的问题。

If methodsA-Z were non-static and virtual you could do this easily, correct? Thus if static methodA-Z were to call non-static, virtual methodA-Z you would be able to override behavior. Knowing this you then need a way to change the instance that contains the non-static versions for testing.

Alternatively, you could take the two classes you want to refactor and make them use a wrapper to this static-only class. Then the two can diverge as much as needed.

That's about all my ideas without looking at the real problem.

山川志 2024-09-11 06:15:33

感谢您的回答和评论。

在查看了有效地处理旧版本中的“打破依赖技术”一章后代码书上,以下两种技术的组合实际上似乎是我们问题的解决方案:

  • Instance Delegator - 用新的虚拟方法包装/替换实用程序类中的静态方法。
  • 静态设置器 - 启用不同实用程序类(生产代码或存根代码)的实例化。

将这两者结合起来将使我们能够将被测组件与产品代码的其余部分分离。

我们现在唯一关心的是性能影响(由于使用虚拟函数)。

Thanks for the answers and comments.

After reviewing "Dependency-Breaking Techniques" chapter from Working Effectively with Legacy Code book, combination of the following two techniques actually seem to be the solution for our problem:

  • Instance Delegator - to wrap/replace static methods from the utility class with new virtual methods.
  • Static Setter - to enable instantiation of a different utility class (either production code or stub code).

Combining these two would enable us to decouple the component under test from the rest of the product code.

Our only concern now is the performance hit (due to use of virtual functions).

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