如何设计一个通用的业务实体而仍然是面向对象的?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (21)

我正在开发一个包装的产品,它应该能够满足具有不同需求的多个客户(在一定程度上),因此应该以一种足够灵活的方式来构建,以便每个特定的客户都可以定制。我们在这里讨论的定制类型是,对于一些关键的业务对象,不同的客户端可能具有不同的属性。此外,它们还可以将不同的业务逻辑与它们的附加属性联系在一起。

作为一个非常简单的例子:将“汽车”视为系统中的一个业务实体,因此具有四个关键属性,即车辆编号、年产、价格和颜色。

使用该系统的客户之一可能会给汽车增加2个属性,即ChassisNumber和EngineCapity。这个客户端需要一些与这些字段相关联的业务逻辑来验证当新汽车被添加时,系统中不存在相同的CassisNumber。

另一个客户机只需要一个名为SaleDate的附加属性。SaleDate有自己的业务逻辑检查,当输入销售日期时,它会验证车辆是否在某些警察记录中不作为被盗车辆存在。

我的大部分经验都是为单个客户端制作企业应用程序,而我真的很难理解如何处理一个属性是动态的,并且具有动态业务逻辑的业务实体,以及在面向对象的范例中具有动态业务逻辑的业务实体。

关键问题

  • 是否有任何一般的面向对象原则/模式可以帮助我解决这种设计?

我相信,在大多数情况下,从事通用/包装产品工作的人都会遇到类似的情况。如有任何建议/指示/一般指导,也将不胜感激。

我的技术是.NET 3.5/C#,该项目有一个具有业务层的分层体系结构,该体系结构由业务实体组成,业务实体包含其业务逻辑

提问于
用户回答回答于

这是我们面临的最大挑战之一,因为我们有多个客户端,它们都使用相同的代码库,但需求差异很大。让我与你们分享我们的进化故事:

我们的公司一开始只有一个客户,当我们开始获得其他客户时,您会开始在代码中看到这样的事情:

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
}

这是一种进步,但仍然很笨重。“魔法弦”比比皆是。没有关于各种属性的真正的组织或文档。许多属性依赖于其他属性,如果没有正确的组合使用,它们不会做任何事情(甚至会破坏某些东西!)。我们在一些迭代中花费了大量(甚至大部分)时间来修复出现的bug,因为我们已经为一个客户端“修复”了一些破坏了另一个客户端配置的东西。当我们得到一个新的客户端时,我们只需要从另一个客户端的属性文件开始,该客户端的配置“最像”这个客户端,然后尝试调整一些东西,直到它们看起来正确为止。

我们尝试使用各种技术使这些配置点不那么笨重,但只取得了适度的进展:

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因为其他客户可能希望在他们的程序中有类似的行为。但是,使用这种方法,您可以相当容易地定义模块,这些模块覆盖特定客户端的默认设置,而且非常灵活和可扩展。

因为像这样的系统本质上是脆弱的,因此也需要集中精力于自动化测试:针对单个类的单元测试,确保(例如)注入绑定都正常工作的集成测试,以及确保所有东西一起工作而不进行倒退的系统测试。

PS:我在整个故事中都使用“我们”,尽管我在公司的历史上并没有真正的工作过。

PPS:原谅C#和Java的混合。

用户回答回答于

当你你在建造,那是动态对象模型或自适应对象模型。当然,当客户开始添加行为和数据时,他们正在编程,因此需要有版本控制、测试、发布、命名空间/上下文和权限管理。

扫码关注云+社区