我最近不得不向我编写的现有应用程序添加一个新功能,并查看这部分代码,意识到可能是时候重构和改进了。
原始索引方法:
对后端 Web 服务的三个 wcf 调用
随着新的添加,我需要一些新信息、一些用户选项。
此功能导致了一个新的 WCF 调用,这使得该方法通过网络进行了 4 次调用。 必须有更好的方法。
我的建议是将其包装到一个 wcf 调用中,以收集有关用户的所有信息,例如他们是否注册、项目、运行规则(如果需要)以及用户选项。
我们只有一个很好的电话,但是我的问题
因此拥有一个对象有意义吗?
如果 IsEnrolled 为 true,则 RulesResult 将为 null,在这种情况下拥有 null 属性是否有意义? 也许提供一个结果,也表明用户已注册,以防稍后检查?
如果 IsEnrolled 为 false,则将填充 RulesResult(有意义),但是 Items 将为 null(有点有意义)用户选项也将为 null 在这种情况下,项目和用户选项的空列表是否比空列表更有意义?
从 api 设计的角度来看,第二个选项是否有意义,或者结果与 UI 密切相关?
两个版本的代码示例:
版本1:
public ActionResult Index()
{
using (var client =ServiceFactory.CreateChannel())
{
var isMemberEnrolled = client.IsMemberEnrolled(User.Identity.Name);
if (!isMemberEnrolled)
{
var accessResult = client.RunRules(User.Identity.Name);
if (!accessResult.UserIsValid)
{
return RedirectToAction("NoAccess");
}
return RedirectToAction("Register");
}
var userOptions = client.GetUserOptions(User.Identity.Name);
List<Item> items = client.GetUserItems(User.Identity.Name);
var viewModel = new ViewModel(userOptions, items);
return View(viewModel);
}
}
版本 2(重构):
public ActionResult Index()
{
using (var client = ServiceFactory.CreateChannel())
{
var userInformation = client.GetUserInformation(User.Identity.Name);
if (!userInformation.IsMemberEnrolled)
{
return RedirectToAction(!userInformation.RulesResult.UserIsValid ? "NoAccess" : "Register");
}
var viewModel = new ViewModel(userInformation.UserOptions, userInformation.Items);
return View(viewModel);
}
}
我认为就 API 效率而言,选项 #2 肯定会表现更好。
就将未使用的参数全部放在一个结果对象中而言,可以使用抽象结果类轻松解决这个问题,然后将两个不同的响应划分为两个不同的具体子类型。
[KnownType( typeof( UserInfoEnrolledResult ) )]
[KnownType( typeof( UserInfoNotEnrolledResult ) )]
[DataContract]
public abstract class UserInfoResult
{
}
[DataContract]
public class UserInfoEnrolledResult : UserInfoResult
{
[DataMember]
public string UserOptions { get; set; }
[DataMember]
public string[] Items { get; set; }
}
[DataContract]
class UserInfoNotEnrolledResult : UserInfoResult
{
[DataMember]
public bool UserIsValid { get; set; }
}
然后你的客户端代码会变成类似...
using ( var client = ServiceFactory.CreateChannel() )
{
var userInformation = client.GetUserInformation( User.Identity.Name );
if ( userInformation is UserInfoNotEnrolledResult )
{
return RedirectToAction( ((UserInfoNotEnrolledResult)userInformation).UserIsValid ? "NoAccess" : "Register" );
}
var enrolledUserInformation = (UserInfoEnrolledResult)userInformation;
var viewModel = new ViewModel( enrolledUserInformation.UserOptions, enrolledUserInformation.Items );
return View( viewModel );
}
这使客户端清楚地知道可能有两种不同的响应,并清楚地表明哪种响应使用或需要哪些参数。
我认为这是一个非常好的方法。 如果您开始遇到案例,发现自己创建了许多不同类型的函数,这些函数的步骤大致相似,但略有不同,例如......
UserInfoResult GetUserInformation( string name );
UserInfoResult GetUserInformationWithoutRuleCheck( string name );
UserInfoResult GetUserInformationWithDoubleSecretChecks( string name );
那么,将这些较大的函数分解为多个 WCF 调用可能是值得的,以确保 API 方法不会激增。