“不要在设计中使用抽象基类;但在建模/分析中" [英] “Do not use Abstract Base class in Design; but in Modeling/Analysis”

查看:32
本文介绍了“不要在设计中使用抽象基类;但在建模/分析中"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

虽然我在 OOAD 方面有一些经验,但我是 SOA 的新手.

I am newbie to SOA though I have some experience in OOAD.

SOA 设计的准则之一是仅将抽象类用于建模.从设计中省略它们".抽象的使用有助于建模(分析阶段).

One of the guidelines for SOA design is "Use Abstract Classes for Modeling only. Omit them from Design". The use of abstraction can be helpful in modeling (analysis phase).

在分析阶段,我提出了一个 BankAccount 基类.从它派生的专门类是FixedAccount"和SavingsAccount".我需要创建一个服务,该服务将为用户返回所有帐户(帐户列表).满足要求的服务结构应该是什么?

During analysis phase I have come up with a BankAccount base class. The specialized classes derived from it are "FixedAccount" and "SavingsAccount". I need to create a service that will return all accounts (list of accounts) for a user. What should be the structure of service(s) to meet the requirement?

注意:如果您能提供使用 WCF 的代码演示,那就太好了.

Note: It would be great if you can provide code demonstration using WCF.

推荐答案

听起来您正在尝试使用 SOA 来远程访问您的对象模型.您最好查看您希望服务公开的交互和功能,并避免公开您的服务实现的继承细节.

It sounds like you are trying to use SOA to remotely access your object model. You would be better of looking at the interactions and capabilities you want your service to expose and avoid exposing inheritance details of your services implementation.

因此,在需要用户帐户列表的情况下,您的界面看起来像

So in this instance where you need a list of user accounts your interface would look something like

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    Collection<AccountSummary> ListAccountsForUser(
        User user /*This information could be out of band in a claim*/);
}

[DataContract]
class AccountSummary
{
     [DataMember]
     public string AccountNumber {get;set;}
     [DataMember]
     public string AccountType {get;set;}
     //Other account summary information
}

如果您决定沿继承路线走下去,您可以使用 KnownType属性,但请注意,这会在通过线路发送的消息中添加一些类型信息,这在某些情况下可能会限制您的互操作性.

if you do decide to go down the inheritance route, you can use the KnownType attribute, but be aware that this will add some type information into the message being sent across the wire which may limit your interoperability in some cases.

更新:

我之前回答的时间有点有限,所以我会试着详细说明我为什么喜欢这种风格.

I was a bit limited for time earlier when I answered, so I'll try and elaborate on why I prefer this style.

我不建议在单独的层中通过 DTO 公开您的 OOAD,这通常会导致接口臃肿,您在其中传递大量未使用的数据并宗教性地将其映射到本质上是您删除了所有逻辑的域模型,我只是看不到价值.我通常围绕它公开的操作设计我的服务层,并使用 DTO 来定义服务交互.

I would not advise exposing your OOAD via DTOs in a seperate layer this usually leads to a bloated interface where you pass around a lot of data that isn't used and religously map it into and out of what is essentially a copy of your domain model with all the logic deleted, and I just don't see the value. I usually design my service layer around the operations that it exposes and I use DTOs for the definition of the service interactions.

使用基于公开操作而非域模型的 DTO 有助于保持服务封装并减少与域模型的耦合.通过不公开我的域模型,我不必为了序列化而对字段可见性或继承做出任何妥协.

Using DTOs based on exposed operations and not on the domain model helps keep the service encapsulation and reduces coupling to the domain model. By not exposing my domain model, I don't have to make any compromises on field visibility or inheritance for the sake of serialization.

例如,如果我将 Transfer 方法从一个帐户公开到另一个帐户,则服务接口将如下所示:

for example if I was exposing a Transfer method from one account to another the service interface would look something like this:

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    TransferResult Transfer(TransferRequest request);
}

[DataContract]
class TransferRequest
{
     [DataMember]
     public string FromAccountNumber {get;set;}
     [DataMember]
     public string ToAccountNumber {get;set;}
     [DataMember]
     public Money Amount {get;set;}
}

class SomeService : ISomeService
{
    TransferResult Transfer(TransferRequest request)
    {
        //Check parameters...omitted for clarity
        var from = repository.Load<Account>(request.FromAccountNumber);
        //Assert that the caller is authorised to request transfer on this account
        var to = repository.Load<Account>(request.ToAccountNumber);
        from.Transfer(to, request.Amount);
        //Build an appropriate response (or fault)
    }
}

现在从这个界面,消费者很清楚调用这个操作所需的数据是什么.如果我将其实现为

now from this interface it is very clear to the conusmer what the required data to call this operation is. If I implemented this as

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    TransferResult Transfer(AccountDto from, AccountDto to, MoneyDto dto);
}

而 AccountDto 是 account 中字段的副本,作为消费者,我应该填充哪些字段?他们都是?如果添加新属性以支持新操作,则所有操作的所有用户现在都可以看到此属性.WCF 允许我将此属性标记为非强制性,这样我就不会破坏所有其他客户端,但如果它对新操作是强制性的,则客户端只会在调用操作时发现.

and AccountDto is a copy of the fields in account, as a consumer, which fields should I populate? All of them? If a new property is added to support a new operation, all users of all operations can now see this property. WCF allows me to mark this property as non mandatory so that I don't break all of my other clients, but if it is mandatory to the new operation the client will only find out when they call the operation.

更糟糕的是,作为服务实施者,如果他们为我提供了当前余额会怎样?我应该相信它吗?

Worse, as the service implementer, what happens if they have provided me with a current balance? should I trust it?

这里的一般规则是问谁拥有数据、客户端或服务?如果客户端拥有它,那么它可以将它传递给服务,并在进行一些基本检查后,服务可以使用它.如果服务拥有它,客户端应该只传递足够的信息供服务检索它需要的信息.这允许服务保持其拥有的数据的一致性.

The general rule here is to ask who owns the data, the client or the service? If the client owns it, then it can pass it to the service and after doing some basic checks, the service can use it. If the service owns it, the client should only pass enough information for the service to retrieve what it needs. This allows the service to maintain the consistency of the data that it owns.

在这个例子中,服务拥有账户信息,定位它的关键是一个账号.虽然服务可能会验证金额(正数、支持的货币等),但该金额归客户所有,因此我们希望填充 DTO 上的所有字段.

In this example, the service owns the account information and the key to locate it is an account number. While the service may validate the amount (is positive, supported currency etc.) this is owned by the client and therefore we expect all fields on the DTO to be populated.

总而言之,我已经看到它以所有 3 种方式完成,但是围绕特定操作设计 DTO 是迄今为止最成功的服务和​​消费者实现.它允许操作独立发展,并且非常明确地说明服务的期望以及返回给客户端的内容.

In summary, I have seen it done all 3 ways, but designing DTOs around specific operations has been by far the most successful both from service and consumer implementations. It allows operations to evolve independently and is very explicit about what is expected by the service and what will be returned to the client.

这篇关于“不要在设计中使用抽象基类;但在建模/分析中"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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