我正在开发一个打包的产品,它应该迎合具有不同需求(在一定程度上)的多个客户,因此应该以一种足够灵活的方式构建,以便每个特定的客户都可以自定义。我们在这里讨论的定制类型是,对于某些关键业务对象,不同的客户端可能具有不同的属性。此外,它们还可以将不同的业务逻辑与其附加属性捆绑在一起
是一个非常简单的例子:认为“汽车”是系统中的一个业务实体,因此有4个关键属性,即VehicleNumber、YearOfManufacture、价格和颜色。
其中一个使用该系统的客户端可能会为汽车添加另外两个属性,即ChassisNumber和EngineCapacity。这个客户端需要一些与这些字段相关联的业务逻辑,以便在添加新的汽车时验证系统中不存在相同的chassisNumber。
另一个客户端只需要一个名为SaleDate的附加属性。SaleDate有自己的业务逻辑检查,当输入销售日期时,该检查将验证车辆是否在某些警方记录中不存在为被盗车辆
我的大部分经验主要是为单个客户端开发企业应用程序,我真的很难理解如何处理一个属性是动态的、并且在面向对象的范例中也具有动态业务逻辑的业务实体
关键问题
我相信从事过通用/包装产品工作的人在大多数情况下都会遇到类似的情况。任何建议/指针/一般指导也是非常感谢的。
我的技术是.NET 3.5/ C#,该项目具有分层的体系结构,其中的业务层由业务实体组成,业务实体包含其业务逻辑
发布于 2011-02-08 08:58:15
这是我们最大的挑战之一,因为我们有多个客户端都使用相同的代码库,但具有广泛不同的需求。让我与你分享我们的进化故事:
我们的公司一开始只有一个客户端,当我们开始获得其他客户端时,你会开始在代码中看到类似这样的东西:
if(clientName == "ABC") {
// do it the way ABC client likes
} else {
// do it the way most clients like.
}
最终,我们明智地认识到,这会使代码变得非常丑陋和难以管理。如果另一个客户希望他们的客户在一个地方表现得像ABC,而CBA在另一个地方,我们就卡住了。因此,我们转而使用具有一堆配置点的.properties文件。
if((bool)configProps.get("LastNameFirst")) {
// output the last name first
} else {
// output the first name first
}
这是一个进步,但仍然很笨拙。“魔法弦”比比皆是。围绕各种属性没有真正的组织或文档。许多属性依赖于其他属性,不会做任何事情(甚至会破坏某些东西!)如果没有使用正确的组合。在一些迭代中,我们的大部分时间(甚至大部分时间)都花在修复错误上,因为我们已经为一个客户端“修复”了一些破坏了另一个客户端配置的东西。当我们得到一个新的客户机时,我们只需要从另一个客户机的属性文件开始,这个客户机的配置“最像”这个客户机想要的那个,然后尝试调整,直到它们看起来正确为止。
我们尝试使用各种技术来使这些配置点变得不那么笨拙,但只取得了一定的进展:
if(userDisplayConfigBean.showLastNameFirst())) {
// output the last name first
} else {
// output the first name first
}
有几个项目可以控制这些配置。其中一个涉及到编写基于XML的视图引擎,以便我们可以更好地为每个客户端定制显示。
<client name="ABC">
<field name="last_name" />
<field name="first_name" />
</client>
另一个项目涉及编写配置管理系统来整合我们的配置代码,确保每个配置点都有良好的文档记录,允许超级用户在运行时更改配置值,并允许代码验证每个更改以避免获得无效的配置值组合。
这些不同的变化无疑让每个新客户端的生活变得容易得多,但它们中的大多数都未能解决问题的根源。真正让我们受益最大的变化是,当我们不再将我们的产品视为一系列的修复,以使某些东西为另一个客户工作时,我们开始将我们的产品视为“产品”。当客户要求提供新功能时,我们开始仔细考虑以下问题:
我们实现了他们所要求的不同功能,该功能仍然可以满足他们的需求,同时更适合被其他客户端重用?
在实现功能时,我们会从长远的角度来考虑。我们可以创建一个全新的表,允许任何客户端定义任意数量的自定义字段,而不是创建一个仅供一个客户端使用的新数据库字段。这将需要更多的前期工作,但我们可以允许每个客户以极大的灵活性定制自己的产品,而不需要程序员更改任何代码。
也就是说,有时如果不在复杂的规则引擎等方面投入大量的精力,就无法真正完成某些定制。当您只需要让它以一种方式为一个客户端工作,以另一种方式为另一个客户端工作时,我发现您最好的选择是使用依赖注入将程序连接到和。如果您遵循“可靠”原则来确保您的代码是模块化编写的,并且具有良好的“关注点分离”等,那么为特定客户端更改代码的特定部分的实现几乎不是那么痛苦:
public FirstLastNameGenerator : INameDisplayGenerator
{
IPersonRepository _personRepository;
public FirstLastNameGenerator(IPersonRepository personRepository)
{
_personRepository = personRepository;
}
public string GenerateDisplayNameForPerson(int personId)
{
Person person = _personRepository.GetById(personId);
return person.FirstName + " " + person.LastName;
}
}
public AbcModule : NinjectModule
{
public override void Load()
{
Rebind<INameDisplayGenerator>().To<FirstLastNameGenerator>();
}
}
我前面提到的其他技术增强了这种方法。例如,我没有编写AbcNameGenerator
,因为其他客户可能希望在他们的程序中有类似的行为。但是使用这种方法,您可以非常容易地定义模块,以一种非常灵活和可扩展的方式覆盖特定客户端的默认设置。
因为这样的系统本质上是脆弱的,所以重点关注自动化测试也很重要:针对单个类的单元测试,确保(例如)注入绑定都正常工作的集成测试,以及确保所有内容协同工作而不会出现性能衰退的系统测试。
附言:我在整个故事中都使用了“我们”,尽管我实际上并没有在公司的大部分历史中工作。
注意:请原谅C#和Java的混合。
发布于 2011-02-08 07:50:09
这是您正在构建的Dynamic Object Model或Adaptive Object Model。当然,当客户开始添加行为和数据时,他们是在编程,因此您需要对其进行版本控制、测试、发布、命名空间/上下文和权限管理。
发布于 2011-01-30 07:19:05
解决这个问题的一种方法是使用元层,或者反射,或者两者都使用。此外,您将需要提供一个自定义应用程序,它将允许用户修改您的业务逻辑层。这样的元层并不真正适合您的分层体系结构-它更像是与现有体系结构正交的层,尽管运行的应用程序可能需要引用它,至少在初始化时。这种类型的工具可能是搞砸人类已知的生产应用程序的最快方法之一,因此您必须:
)
您的元层中将包含对象,如业务对象、方法、属性和事件,如添加业务对象、调用方法等。
web上有大量关于元编程的信息,但我会从Program Design Vol 2的模式语言或任何与Kent或Coplien相关或源自Kent或Coplien的WWW资源开始。
https://stackoverflow.com/questions/4838845
复制相似问题