如何设计一个通用商业实体,仍然是OO? [英] How to Design a generic business entity and still be OO?

查看:149
本文介绍了如何设计一个通用商业实体,仍然是OO?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一种应用于不同需求的多个客户端的打包产品(在一定程度上),因此应以足够灵活的方式构建,以便每个特定客户端可定制。我们在这里谈论的那种定制是不同的客户端可能对某些关键业务对象有不同的属性。此外,他们可能会有不同的业务逻辑与他们的额外属性绑定



作为一个非常简单的例子: 考虑汽车成为系统中的商业实体,因此具有4个关键属性,即VehicleNumber,YearOfManufacture,Price and Color。



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



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



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



主要问题




  • 有没有一般的OO原则/模式,可以帮助我解决这样的设计?



我相信在泛型/包装产品上工作的人在大多数情况下都会遇到类似的情况。任何建议/指针/一般指导也不胜感激。



我的技术是.NET 3.5 / C#,该项目具有分层架构,其业务层包含包含业务逻辑的业务实体

解决方案

这是我们面临的最大挑战之一,因为我们拥有多个客户所有这些都使用相同的代码库,但是有着广泛的需求。让我与你分享我们的演变故事:



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

  if(clientName ==ABC){
//以ABC客户端的方式喜欢
} else {
//这样做大多数客户喜欢的方式。
}

最终我们明白了这个事实,这使得真的很丑陋,无法管理的代码。如果另一个客户希望他们的行为像ABC在一个地方,CBA在另一个地方,我们被困住了。所以我们转而使用一组配置点的.properties文件。

  if((bool)configProps.get LastNameFirst)){
//输出最后一个名字
} else {
//先输出名字
}

这是一个改进,但仍然很笨重。 魔术弦很多。各种物业都没有真正的组织或文件。许多属性取决于其他属性,如果没有在正确的组合中使用,也不会做任何事情(甚至会打破某些事情!)。在一些迭代中,很多(甚至大多数时候)花费了修复出现的错误,因为我们为一个客户端修复了一些破坏另一个客户端配置的东西。当我们得到一个新的客户端时,我们将从另一个客户端的属性文件开始,该客户端的配置最喜欢这个客户端所需的配置,然后尝试调整一切,直到它们正确。



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

  if(userDisplayConfigBean.showLastNameFirst())){
//输出最后一个名字
} else {
//输出名字首先
}

有几个项目可以控制这些配置。一个涉及编写基于XML的视图引擎,以便我们可以更好地为每个客户端定制显示。

 < client name = ABC > 
< field name =last_name/>
< field name =first_name/>
< / client>

另一个项目涉及编写配置管理系统来整合我们的配置代码,强制每个配置点都很好记录,允许超级用户在运行时更改配置值,并允许代码验证每个更改,以避免获得无效的配置值组合。



这些各种每个新客户的变化肯定使生活更容易,但大多数人未能解决我们问题的根源。真正让我们受益最多的变化是当我们停止将产品视为一系列修复措施,为一个客户做出一些工作,我们开始将产品视为产品。当客户要求提供新功能时,我们开始仔细考虑以下问题:




  • 有多少其他客户端可以使用此功能现在还是将来?

  • 可以以不使代码不易管理的方式实现吗?

  • 我们可以实现一个不同的功能,他们要求什么,这仍然可以满足他们的需求,同时更适合其他客户端的重用?



当实施一个功能,我们将采取长远的看法。而不是创建一个仅由一个客户端使用的新数据库字段,我们可能会创建一个全新的表,可以允许任何客户端定义任意数量的自定义字段。这将需要更多的工作,但我们可以允许每个客户端以很大的灵活性自定义自己的产品,而不需要程序员更改任何代码。



那就是说,有时候有一些定制化你无法真正完成,而不必在复杂的规则引擎等方面投入大量的精力。当您只需要为一个客户端提供一种方式,另一种方式为另一客户端工作时,我发现您最好的选择是使用接口利用依赖注入。如果您遵循SOLID原则,以确保您的代码是以良好的分离问题等模块化编写的,那么为特定客户端更改特定部分代码的实现并不那么痛苦: / p>

  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> ()。为了< FirstLastNameGenerator>();
}
}

这种方法是通过我之前提到的其他技术来增强的。例如,我没有写一个 AbcNameGenerator ,因为也许其他客户端将在他们的程序中想要类似的行为。但是使用这种方法,您可以很容易地定义覆盖特定客户端的默认设置的模块,方式非常灵活和可扩展。



因为像这样的系统本身就是脆弱的,同样重要的是重点关注自动化测试:单个类的单元测试,集成测试以确保(例如)您的注入绑定都正常工作,以及系统测试,以确保一切都可以一起工作而不会退化。 p>

PS:我在这个故事中使用我们,尽管我实际上并没有在公司工作的大部分历史。



PPS:赦免C#和Java的混合。


I am working on a packaged product that is supposed to cater to multiple clients with varying requirements (to a certain degree) and as such should be built in a manner to be flexible enough to be customizable by each specific client. The kind of customization we are talking about here is that different client's may have differing attributes for some of the key business objects. Also, they could have differing business logic tied in with their additional attributes as well

As an very simplistic example: Consider "Automobile" to be a business entity in the system and as such has 4 key attributes i.e. VehicleNumber, YearOfManufacture, Price and Colour.

It is possible that one of the clients using the system adds 2 more attributes to Automobile namely ChassisNumber and EngineCapacity. This client needs some business logic associated with these fields to validate that the same chassisNumber doesnt exist in the system when a new Automobile gets added.

Another client just needs one additional attribute called SaleDate. SaleDate has its own business logic check which validates if the vehicle doesnt exist in some police records as a stolen vehicle when the sale date is entered

Most of my experience has been in mostly making enterprise apps for a single client and I am really struggling to see how I could handle a business entity whose attributes are dynamic and also has a capacity for having dynamic business logic as well in an object oriented paradigm

Key Issues

  • Are there any general OO principles/patterns that would help me in tackling this kind of design?

I am sure people who have worked on generic / packaged products would have faced similar scenarios in most of them. Any advice / pointers / general guidance is also appreciated.

My technology is .NET 3.5/ C# and the project has a layered architecture with a business layer that consists of business entities that encompass their business logic

解决方案

This is one of our biggest challenges, as we have multiple clients that all use the same code base, but have widely varying needs. Let me share our evolution story with you:

Our company started out with a single client, and as we began to get other clients, you'd start seeing things like this in the code:

if(clientName == "ABC") {
    // do it the way ABC client likes
} else {
    // do it the way most clients like.
}

Eventually we got wise to the fact that this makes really ugly and unmanageable code. If another client wanted theirs to behave like ABC's in one place and CBA's in another place, we were stuck. So instead, we turned to a .properties file with a bunch of configuration points.

if((bool)configProps.get("LastNameFirst")) {
    // output the last name first
} else {
    // output the first name first
}

This was an improvement, but still very clunky. "Magic strings" abounded. There was no real organization or documentation around the various properties. Many of the properties depended on other properties and wouldn't do anything (or would even break something!) if not used in the right combinations. Much (possibly even most) of our time in some iterations was spent fixing bugs that arose because we had "fixed" something for one client that broke another client's configuration. When we got a new client, we would just start with the properties file of another client that had the configuration "most like" the one this client wanted, and then try to tweak things until they looked right.

We tried using various techniques to get these configuration points to be less clunky, but only made moderate progress:

if(userDisplayConfigBean.showLastNameFirst())) {
    // output the last name first
} else {
    // output the first name first
}

There were a few projects to get these configurations under control. One involved writing an XML-based view engine so that we could better customize the displays for each client.

<client name="ABC">
    <field name="last_name" />
    <field name="first_name" />
</client>

Another project involved writing a configuration management system to consolidate our configuration code, enforce that each configuration point was well documented, allow super users to change the configuration values at run-time, and allow the code to validate each change to avoid getting an invalid combination of configuration values.

These various changes definitely made life a lot easier with each new client, but most of them failed to address the root of our problems. The change that really benefited us most was when we stopped looking at our product as a series of fixes to make something work for one more client, and we started looking at our product as a "product." When a client asked for a new feature, we started to carefully consider questions like:

  • How many other clients would be able to use this feature, either now or in the future?
  • Can it be implemented in a way that doesn't make our code less manageable?
  • Could we implement a different feature that what they are asking for, which would still meet their needs while being more suited to reuse by other clients?

When implementing a feature, we would take the long view. Rather than creating a new database field that would only be used by one client, we might create a whole new table which could allow any client to define any number of custom fields. It would take more work up-front, but we could allow each client to customize their own product with a great degree of flexibility, without requiring a programmer to change any code.

That said, sometimes there are certain customizations that you can't really accomplish without investing an enormous effort in complex Rules engines and so forth. When you just need to make it work one way for one client and another way for another client, I've found that your best bet is to program to interfaces and leverage dependency injection. If you follow "SOLID" principles to make sure your code is written modularly with good "separation of concerns," etc., it isn't nearly as painful to change the implementation of a particular part of your code for a particular 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>();
     }
}

This approach is enhanced by the other techniques I mentioned earlier. For example, I didn't write an AbcNameGenerator because maybe other clients will want similar behavior in their programs. But using this approach you can fairly easily define modules that override default settings for specific clients, in a way that is very flexible and extensible.

Because systems like this are inherently fragile, it is also important to focus heavily on automated testing: Unit tests for individual classes, integration tests to make sure (for example) that your injection bindings are all working correctly, and system tests to make sure everything works together without regressing.

PS: I use "we" throughout this story, even though I wasn't actually working at the company for much of its history.

PPS: Pardon the mixture of C# and Java.

这篇关于如何设计一个通用商业实体,仍然是OO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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