【问题标题】:How to design relation between a class Bill and a class SimplifiedBill?如何设计类 Bill 和类 SimplifiedBill 之间的关系?
【发布时间】:2019-11-19 10:51:46
【问题描述】:

如标题所述,我有SimplifiedBill,它只有datefinalPayment,我还有Bill,它与@ 具有相同的属性987654323@,还有所有其他详细信息(税收用户数据服务等)。

一个解决方案可能是BillSimplifiedBill 继承,但我认为这在概念上不正确,因为SimplifiedBill 是你从Bill 得到的东西,所以Bill 不是什么东西“低于”SimplifiedBill,它不会继承任何东西。

关于如何在这里实现类设计有什么想法吗?

编辑: 过程如下。
我有一个从后面得到的SimplifiedBill 列表,当我点击其中一个时,我请求并从 api 中获取 Bill 并进入详细视图。

如果您想知道,我正在使用 Angular,并且我有一个 ListComponent(或视图),其中有我的 SimpleBill 对象,当我单击其中一个对象时,我会转到 DetailComponent ,在那里我可以看到特定的Bill。当我从一个视图转到另一个视图时,我无法传输对象 SimpleBill,而只能传输其 id(或其他字段)。

【问题讨论】:

  • @juzraai 这也是我的想法,但日期和 finalPayment 并不是真正的账单,我认为这会误导任何阅读代码的人

标签: javascript typescript inheritance entity-relationship class-design


【解决方案1】:

不,我同意,从 SimplifiedBill 继承在概念上是不正确的。 一般来说,我认为谨慎使用继承是可取的。我很少看到它正确使用。

相反,我会推荐一种组合方法:如果 Bill 包含 SimplifiedBill,那么我认为拥有一个具有细节属性的数据结构 Bill 是有意义的:

let bill = {
  details: {...},
  simplifiedBill: {...}
}

请注意,这里我使用了一个简单的对象(正如您使用 javascript 一样,它可能也值得考虑),但它同样适用于基于类的方法。 不过,您可能想为简化的账单想一个不同的名称。也许像“摘要”之类的东西。

但总的来说,您认为继承的概念可能不太适合这里。

【讨论】:

  • 感谢您的回答。我正在使用 Typescript,所以它必须是基于类的方法。我也考虑过这个解决方案,但问题是,在对 API 的第一次请求中,我得到了很多 SimplifiedBill 对象,当我访问其中一个对象时,我会得到那个特定 SimplifiedBill 的所有详细信息.所以我们回到第一个问题,因为即使我使用组合,我也必须构建这两个不同的对象/实体,我还不知道如何关联。
  • 即使在打字稿中,您也没有使用类;)。您究竟是什么意思“当我访问简化账单时”?你的意思是你有某种账单的详细视图,对吧?即使在那种情况下,我描述的方法也很好。事实上,我可以想象一个列表请求中有很多摘要会在点击时加载它们的详细数据。根据您的观点(真的很难说),您还应该考虑完全不关联这些对象。当然,它们在某种程度上是相关的。但它们不必在代码中相关。
  • 是的,我有一个SimplifiedBill 列表,当我单击其中一个时,我会进入您提到的Bill 的详细视图。如果我使用您的解决方案,我将不得不动态创建一个账单(一开始,我不会有任何细节)。我不是说这是坏事,只是大声思考。我也同意你所说的根本不必把它们联系起来。我正在尝试在这两种解决方案之间做出决定。
  • 我会考虑从你所说的中选择什么选项,但我认为它们是正确的。我编辑了您的答案以包含“不相关”选项:)
【解决方案2】:

我倾向于认为您可能甚至不需要在这里建立关系;听起来SimplifiedBill 类更像是Bill 类的简单投影。

解决此问题的一种方法是将两个类完全分开,并引入第三个类,例如SimplifiedBillFactory,它采用Bill 并产生SimplifiedBill

例如(伪代码,可能不是有效的打字稿!)

export class SimplifiedBillFactory
{

  createSimplifiedBill(bill: Bill) : SimplifiedBill
  {
     return new SimplifiedBill{
             billId=bill.Id,
             date=bill.date,
             finalPayment=bill.calculateFinalPayment()
     };         
   }
}

这种方法的一个优点是,如果您需要扩充SimplifiedBill 以包含更多详细信息(例如某些聚合,例如订单项的总数等),在本课程中很容易做到这一点。此外,它鼓励您将账单的实际行为保留在 Bill 类本身中,并通过方法(例如 calculateFinalPayment 上的 calculateFinalPayment 方法)公开它们

您也可以只在 Bill 类上使用工厂方法而不是单独的工厂类,但我个人更愿意将其分开,例如,如果您以后需要使用其他类Bill 类,用于创建您的 SimplifiedBill 实例。

【讨论】:

  • 这是一个不错的选择,但我不能在我的情况下使用它,因为 SimplifiedBill 直接来自后面,甚至在没有任何 Bill 之前
  • @JaviMarzán 好吧,我明白了。但是在那种情况下,我不确定问题的前提是什么,我的意思是,在这种情况下,它们似乎根本不需要相关,您是否正在尝试实现与Bill 相关的特定目标和SimplifiedBill?如果SimplifiedBill 仅用作后端查找(通过billId 或其他),那么我想说你根本不需要担心将这两件事联系起来:)
  • 是的,我同意,@LucasManzke 也刚刚说过,我认为最好不要将它们联系起来。谢谢!正如他之前所说,我会接受他的回答,但非常感谢:)
【解决方案3】:

有趣的问题,

继承是一种is-a关系。在创建它们之间的继承关系时,问问自己 A 是否为 B。

  • 第一个问题:什么是最简单的法案或法案至少应该有什么?答案还回答了Bill 应该是什么。

由于SimpleBill也是Bill的一种,所以SimpleBill的子集使得Bill,Bill本身可以简单到只有一个billId(根据cmets)。所以Bill 将是:

interface Bill{
    billId: string;
}
  • 第二个问题:VerboseBillSimpleBill 还是相反?,如果它们之间存在继承关系,则答案也回答了。

奇怪的是SimpleBillVerboseBill或者相反,所以它们没有继承关系;

以后你可能还有其他的账单,比如AaaBill{taxes}BbbBill{services}...,它们有继承关系吗?但他们都是Bill

我的建议:

export interface SimpleBill extends Bill{
  date: number;
  finalPayment: number;

}

export interface VerboseBill extends Bill{
    date: number;
    finalPayment: number;
    taxes:number;
    services;...
    userData;...
}

export namespace Bill{

    export function simplify(bill:Bill): SimpleBill{
        return {id: bill.id, date: bill.date, finalPayment: bill.finalPayment};
    }

    export function verbose(bill:Bill): VerboseBill{
        if((bill as VerboseBill).taxes !==undefined){
            return bill as VerboseBill;
        }
        return fetchVerboseBill(bill.id);
    }
}

export namespace SimpleBill{
    export const verbose = Bill.verbose
}

export namespace VerboseBill{
    export const simplify = Bill.simplify
}

【讨论】:

  • 你的回答很有趣,我很喜欢。但是,存在不允许此解决方案的限制。我正在使用 Angular,当我单击 SimplifiedBill 时,我会转到其他视图/组件。问题是,当我这样做时,我只能将一些东西作为 billid 传递给新的 component 作为参考,所以我不能在新视图中做 SimpleBill.verbose(bill)。最好使用我作为参考传递的 id,我只需得到 VerboseBill。所以我认为这里最好的解决方案是根本不关联它们,正如其他两个贡献者所说(我会接受@LucasManzke 的回答)
  • 不管怎样,谢谢你的回答,我发现你的方法很有趣
  • 那么就是说,Bill 本身可以简单到只包含一个billId,比如interface Bill{ id:string},然后就可以使用Bill.verbose(bill:Bill) 方法得到VerboseBill。跨度>
  • @JaviMarzán 您最好通过包含您在 cmets 中提到的详细信息来更新您的问题。
  • 不,因为在ListComponent(或视图)中我有一些对象,而在DetailComponent 中我会有另一个。我无法将SimpleBill 从一个视图转移到另一个视图。当我进去时,我在DetailComponent 中唯一拥有的是billId,所以用它来获取SimpleBill 是没有意义的,然后用那个账单获取VerboseBill。用我得到的BillId 简单地获取VerboseBill 更简单。另外,我不太同意您提到的仅带有 id 的实体 Bill,这对我来说没有意义。
猜你喜欢
  • 2019-09-16
  • 2012-03-21
  • 1970-01-01
  • 2012-04-02
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
  • 2021-03-29
  • 1970-01-01
相关资源
最近更新 更多