“编程到接口”使用扩展方法:什么时候走得太远? [英] "Program to an interface" using extension methods: When does it go too far?

查看:136
本文介绍了“编程到接口”使用扩展方法:什么时候走得太远?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:本着程序到界面,而不是实现 Haskell类型类,作为一个编码实验,我正在考虑创建一个主要基于接口和扩展方法组合的API意味着什么。我有两条准则:

Background: In the spirit of "program to an interface, not an implementation" and Haskell type classes, and as a coding experiment, I am thinking about what it would mean to create an API that is principally founded on the combination of interfaces and extension methods. I have two guidelines in mind:


  1. 尽可能避免类继承。接口应该实现为密封类 es。

    (这有两个原因:首先,因为子类化提出了一些关于如何在派生类中指定和强制执行基类'契约。其次,这就是Haskell类型类的影响,多态不需要子类化。)

尽可能避免使用实例方法。如果可以使用扩展方法完成,那么这些是首选。

(这是为了帮助保持接口紧凑:可以通过其他实例方法的组合完成的所有操作成为扩展方法。界面中剩下的是核心功能,特别是状态改变方法。)

Avoid instance methods wherever possible. If it can be done with extension methods, these are preferred.
(This is intended to help keep the interfaces compact: Everything that can be done through a combination of other instance methods becomes an extension method. What remains in the interface is core functionality, and notably state-changing methods.)

问题:我遇到了第二条准则的问题。考虑一下:

Problem: I am having problems with the second guideline. Consider this:

interface IApple { }
static void Eat(this IApple apple)
{
    Console.WriteLine("Yummy, that was good!");
}

interface IRottenApple : IApple { }
static void Eat(this IRottenApple apple)
{
    Console.WriteLine("Eat it yourself, you disgusting human, you!");
}

sealed class RottenApple : IRottenApple { }
IApple apple = new RottenApple();
// API user might expect virtual dispatch to happen (as usual) when 'Eat' is called:
apple.Eat(); // ==> "Yummy, that was good!"

显然,预期结果(自己吃吧......),应该是常规实例方法。

Obviously, for the expected outcome ("Eat it yourself…"), Eat ought to be a regular instance method.

问题:关于扩展方法与(虚拟)实例方法的使用,哪些是精确/更准确的指南?什么时候使用扩展方法编程到接口太过分了?在什么情况下实际需要实例方法?

Question: What would be a refined / more accurate guideline about the use of extension methods vs. (virtual) instance methods? When does the use of extension methods for "programming to an interface" go too far? In what cases are instance methods actually required?

我不知道是否任何明确的一般规则,所以我没想到一个完美的,普遍的答案。对上述准则(2)的任何有争议的改进都表示赞赏。

I don't know if there is any clear, general rule, so I am not expecting a perfect, universal answer. Any well-argued improvements to guideline (2) above are appreciated.

推荐答案

你的指南很好,因为它已经很好了说尽可能。所以我的任务是在一些更详细的信息中拼出尽可能的位。

Your guideline is good enough as it is: it already says "wherever possible". So the task is really to spell out the "wherever possible" bit in some more details.

我使用这个简单的二分法:如果添加方法的目的是隐藏子类之间的差异,使用扩展方法;如果目的是突出显示差异,请使用虚拟方法。

I use this simple dichotomy: if the purpose of adding a method is to hide the differences among subclasses, use an extension method; if the purpose is to highlight the differences, use a virtual method.

您的方法是一个示例在子类之间引入差异的方法:吃(或不吃)苹果的过程取决于它是什么类型的苹果。因此,您应该将其实现为实例方法。

Your Eat method is an example of a method that introduce a difference among subclasses: the process of eating (or not) an apple depends on what kind of apple it is. Therefore, you should implement it as an instance method.

尝试隐藏差异的方法示例是 ThrowAway

An example of a method that tries to hide the differences would be ThrowAway:

public static void ThrowAway(this IApple apple) {
    var theBin = RecycleBins.FindCompostBin();
    if (theBin != null) {
        theBin.Accept(apple);
        return;
    }
    apple.CutUp();
    RecycleBins.FindGarbage().Accept(apple);
}

如果抛弃一个苹果的过程是相同的,无论哪种苹果,该操作是在扩展方法中实现的主要候选者。

If the process of throwing away an apple is the same regardless of the kind of the apple, the operation is a prime candidate for being implemented in an extension method.

这篇关于“编程到接口”使用扩展方法:什么时候走得太远?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆