“流畅”(或可链式调用)的方法是否必须是不可变的?

15

假设我有一个类,它有一些属性和一些用于操作这些属性的方法:

public class PersonModel
{
    public string Name { get; set; }
    public string PrimaryPhoneNumber { get; set; }

    public void LoadAccountInfo(AccountInfo accountInfo)
    {
        this.Name = accountInfo.Name;
    }

    public void LoadPhoneInfo(PhoneInfo phoneInfo)
    {
        this.PrimaryPhoneNumber = phoneInfo.PhoneNumber;
    }
}

典型使用方法如下:

var model = new PersonModel();
model.LoadAccountInfo(accountInfo);
model.LoadPhoneInfo(phoneInfo);

我认为让这些方法可以链式调用会很酷:

    public PersonModel LoadAccountInfo(AccountInfo accountInfo)
    {
        this.Name = accountInfo.Name;
        return this;
    }

    public PersonModel LoadPhoneInfo(PhoneInfo phoneInfo)
    {
        this.PrimaryPhoneNumber = phoneInfo.PhoneNumber;
        return this;
    }

那么使用方法将是:

var model = new PersonModel()
    .LoadAccountInfo(accountInfo)
    .LoadPhoneInfo(phoneInfo);

但是在这些可链式方法中,我并没有返回一个修改后的传入PersonModel对象的"克隆"。它们只是修改原始对象并将其返回(为方便起见)。对我来说,这会产生一种歧义,因为调用这些方法的人可能会假设它们是不可变的(即它们保留了原始对象但返回了一个修改后的对象)。

那么这是否违反了有关流畅/可链接接口的最佳实践?


2
StringBuilder是一个很好/众所周知的例子,证明这不是事实。 - Marc Gravell
那不就是流畅接口的精确目标吗? - user703016
1
这不是一个流畅的接口。这是方法链式调用。 - Rasmus Faber
5个回答

4

我个人认为不可变性和流畅接口是两个独立的问题。你可能会发现,不可变性在一般情况下是一种好的东西,也是最佳实践。但这与你是否使用流畅接口无关。


4
不,改变传递的对象是完全合理的。Fluent NHibernate 的方法都是这样工作的(例如,当它们修改 FluentConfiguration 对象时)。
但这也不是一个规则。String 是最好的不可变的对象示例,你可以链接一堆方法,每个方法都返回一个新的实例。

1
不可变对象产生较少的副作用。可变对象更为普遍,因此可能更易于理解。+1 表示展示双重方式的示例。 - Brian Genisio
1
正确。另一个展现不可变对象在流畅接口中使用的很好的例子是Java的Joda Time。两种方式都可以。 - Ray Toal

1

+1篇文章。虽然我认为Ray总结了答案,谢谢! - user718642

0

对象是引用类型,当你专门要求它“返回这个”时,为什么会期望它返回不同的东西。

不违反任何最佳实践。事实上,除非你有理由,否则没有必要克隆一个对象。


1
我不明白你在建议什么。不可变类型非常普遍。例如,String是不可变的。调用"foo".Trim().Substring(1)是作为不可变实现的。选择这样做更多地与您想要构建数据对象的方式有关。从功能角度来看,这样做有很多价值。 - Brian Genisio
我并不否认不可变性的好处,我的观点仅仅是如果没有必要,就没有必要实现它。仅仅因为某个对象被认为是“不可变”的原因似乎还不足够。 - saj

0

流畅方法的魅力在于返回相同修改后的对象。


1
我不确定为什么“所有的魅力”依赖于它是相同的对象。虽然我认为对象不需要是不可变的,但我不明白流畅接口的魅力与被链接的对象的可变性有任何关系。在我看来,“所有的魅力”在于接口非常易读。 - Brian Genisio
@Brian,我的意思是,对于流畅类的用户来说,获得相同的对象而不是副本是显然/自然的,在我看来。 - Kirill Polishchuk
1
LINQ可能是最常用的流畅接口,它返回一个新对象。我认为其魅力在于返回相同类型的对象。 - JamesF
1
我同意@JamesF的观点。所有LINQ扩展方法都会创建一个应用了更改的新实例,因此状态始终是不可变的。然而,LINQ是一个美丽流畅接口的绝佳示例。 - Matias Cicero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接