问题 编程一对多关系
所以我很惊讶在谷歌和stackoverflow上搜索不会返回更多结果。
在OO编程中(我使用的是java),你如何正确地实现一对多关系?
我上课了 Customer
和班级 Job
。我的申请是为一家为客户完成工作的虚构公司。我目前的实施是这样的 Job
上课没有任何关系 Customer
上课时,完全没有提及它。该 Customer
class使用集合和方法来保存,检索和修改有关已为客户分配和/或完成的作业的信息。
问题是,如果我想知道哪个客户是特定的,该怎么办? Job
已经完成?我只发现这篇文章是相关的: http://www.ibm.com/developerworks/webservices/library/ws-tip-objrel3/index.html。
根据作者的实施,我会让 Job
构造函数拿一个 Customer
参数,并存储它,以便我可以检索它。但是,我完全不能保证这个模型可以 一贯。没有为工作的相关客户设置工作作为该工作不适用的客户,并为其他人完成的客户添加工作。任何有关这方面的帮助将不胜感激。
8700
2018-04-10 09:52
起源
答案:
没有100%可靠的方法来维护完整性。
通常采用的方法是使用一种方法来构建关系,并在同一方法中构建另一个方向。但是,正如你所说,这并不能阻止任何人搞乱它。
下一步是使一些方法可以访问包,这样至少与你的无关的代码不能破坏它:
class Parent {
private Collection<Child> children;
//note the default accessibility modifiers
void addChild(Child) {
children.add(child);
}
void removeChild(Child) {
children.remove(child);
}
}
class Child {
private Parent parent;
public void setParent(Parent parent){
if (this.parent != null)
this.parent.removeChild(this);
this.parent = parent;
this.parent.addChild(this);
}
}
实际上,您不会经常在课堂上模拟这种关系。相反,您将在某种存储库中查找所有子项以查找父项。
9
2018-04-10 11:02
答案:
没有100%可靠的方法来维护完整性。
通常采用的方法是使用一种方法来构建关系,并在同一方法中构建另一个方向。但是,正如你所说,这并不能阻止任何人搞乱它。
下一步是使一些方法可以访问包,这样至少与你的无关的代码不能破坏它:
class Parent {
private Collection<Child> children;
//note the default accessibility modifiers
void addChild(Child) {
children.add(child);
}
void removeChild(Child) {
children.remove(child);
}
}
class Child {
private Parent parent;
public void setParent(Parent parent){
if (this.parent != null)
this.parent.removeChild(this);
this.parent = parent;
this.parent.addChild(this);
}
}
实际上,您不会经常在课堂上模拟这种关系。相反,您将在某种存储库中查找所有子项以查找父项。
9
2018-04-10 11:02
也许你没想到复杂(和零代码)的答案,但是 没有解决方案来按照您的意图构建您的防弹API。并不是因为范例(OO)或平台(Java),而是因为你做了错误的分析。在交易世界中(每个系统都能模拟现实生活中的问题及其随时间的演变) 是交易的)这段代码 永远不会破裂 在某一点:
// create
Job j1 = ...
Job j2 = ...
...
// modify
j1.doThis();
...
// access
j2.setSomeProperty(j1.someProperty);
因为当时 j1.someProperty
访问, j1
和 j2
甚至不存在:)
TL; DR
对此的答案很长 不变性,它还介绍了概念 生命周期 和 交易。所有其他答案告诉你 怎么做 它,而不是我想要概述 为什么。一对多关系有两个方面
- 有很多
- 属于
只要客户,您的系统就是一致的 A
有工作 B
, 工作 B
属于客户 A
。您可以通过多种方式实现此目的,但这必须在a中实现 交易,即由简单的复杂动作组成,系统必须是 unavailble 直到事务完成执行。这看起来太抽象而且与你的问题无关吗?不,它不是:)事务系统确保客户端只有在所有这些对象都在的情况下才能访问系统的对象 有效的国家因此,只有整个系统是一致的。从其他答案中,您可以看到需要解决的处理量 一些 问题,所以保证是有代价的: 性能。这就是为什么Java(和其他通用OO语言)无法解决您的问题的简单解释 盒子外面。
当然,OO语言可用于模拟a 交易世界 并且访问它,但必须特别小心,必须施加一些约束,并且客户端开发人员需要特殊的编程风格。通常,事务系统提供两个命令: 搜索 (又名查询)和 锁。查询的结果是 一成不变:它是系统拍摄时的一张照片(即副本),修改照片显然对现实世界没有任何影响。如何修改系统?平时
- 如果/需要,锁定系统(或部分系统)
- 找到一个对象:返回一个 复制 (照片)可以在本地读取和写入的真实对象
- 修改本地副本
- 提交修改后的对象,即让系统根据提供的输入更新其状态
- 丢弃对(现在无用的)本地对象的任何引用:系统已更改已更改,因此本地副本不是最新的。
(顺便说一句,你能看到生命周期的概念如何应用于本地和远程对象吗?)
你可以去 Set
S, final
修饰符等,但在你引入交易和不变性之前,你的设计会有一个缺陷。通常,Java应用程序由数据库提供支持,数据库提供事务功能,并且DB通常与ORM(例如Hibernate)耦合以编写面向对象的代码。
4
2018-04-10 16:28
您可以使用a确保没有重复项 组 实施就像 HashSet的 而不是使用其他数据结构。
而不是添加 工作 对于客户,在Job类中创建一个具有私有构造函数的最终内部类。这确保了包装器内部类只能由作业对象创建。让你的Job构造函数接受 JOBID 和客户作为参数。为了保持一致性 - 如果客户是Null抛出异常,则不应创建虚拟作业。
在 加 客户的方法,检查是否 工作 包裹着 JobUnit如果没有抛出异常,则具有与其自己的id相同的客户ID。
在Job类中替换客户时删除 JobUnit 使用Customer类提供的方法并将其自身添加到新客户并将客户引用更改为新传递的客户。
这样你可以更好地推理你的代码。
这是您的客户类可能是什么样子。
public class Customer {
Set<JobUnit> jobs=new HashSet<JobUnit>();
private Long id;
public Customer(Long id){
this.id = id;
}
public boolean add(JobUnit unit) throws Exception{
if(!unit.get().getCustomer().id.equals(id))
throw new Exception(" cannot assign job to this customer");
return jobs.add(unit);
}
public boolean remove(JobUnit unit){
return jobs.remove(unit);
}
public Long getId() {
return id;
}
}
工作班:
public class Job {
Customer customer;
private Long id;
最终JobUnit单位;
public Job(Long id,Customer customer) throws Exception{
if(customer==null)
throw new Exception("Customer cannot be null");
this.customer = customer;
unit= new JobUnit(this);
this.customer.add(unit);
}
public void replace(Customer c) throws Exception{
this.customer.remove(unit);
c.add(unit);
this.customer=c;
}
public Customer getCustomer(){
return customer;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
public final class JobUnit{
private final Job j;
private JobUnit(Job j){
this.j = j;
}
public Job get(){
return j;
}
}
}
但有一点我很好奇,为什么你甚至需要为客户对象添加工作?
如果要检查的是要查看哪个客户已分配到哪个作业,只需检查作业即可获得该信息。一般来说,除非不可避免,否则我尽量不创建循环引用。
此外,如果不需要在创建工作时替换客户,只需将客户字段设置为最终 工作 class和remove方法 组 要么 更换 它。
应该在数据库中维护为作业分配客户的限制,并且应该将数据库条目用作检查点。
至于为客户添加为其他人完成的工作,您可以检查工作中的客户参考,以确保添加工作的客户与其拥有的工作相同,甚至更好 - 只需删除客户对Job的任何引用,它将简化您的工作。
2
2018-04-10 14:16
如果Customer对象拥有该关系,那么您可以这样做:
Job job = new Job();
job.setStuff(...);
customer.addJob(Job job) {
this.jobs.add(job);
job.setCustomer(this); //set/overwrite the customer for this job
}
//in the job class
public void setCustomer(Customer c) {
if (this.customer==null) {
this.customer = c;
} // for the else{} you could throw a runtime exception
}
...如果所有权是相反的方式,只需替代客户的工作。
这个想法是让关系的所有者保持一致性。双向关系通常意味着一致性管理位于两个实体中。
1
2018-04-10 10:09
做一个适当的setter功能,保持一致性。例如,无论何时创建作业,都要在构造函数中提供客户。然后,作业构造函数将自己添加到客户的作业列表中。
或者,无论何时向客户添加作业,添加功能都必须检查作业的客户是否是要添加到的客户。
或者这个和类似的东西的组合,以满足您的需求。
0
2018-04-10 10:06
只需在包含其他对象的对象中实现某种集合
例如,客户可以说:
private List<Job> jobs;
然后通过使用getter和setter,您可以将值作业添加到此列表中。
这是基本的OO东西,我认为你没有在互联网上搜索得足够多。有很多关于这些主题的信息。
顺便说一句,你可以使用所有类型的集合(集合,列表,地图)
-1
2018-04-10 10:27
我知道这已经很晚了,但我认为另一种方法是稍微改变一下这个问题。由于客户持有由客户分配或完成的所有工作的集合,因此您可以将工作类视为客户的子类,其中包含客户完成所有工作的额外信息。然后,您只需要在主类中维护客户ID,它就会被继承。此设计将确保每个作业都可以链接到客户。此外,如果对于客户,您想要了解有多少工作也可以获得。
-1
2017-10-18 20:48
对不起,我知道这已经很晚了,但我遇到了类似的问题,我认为最好的解决方案是遵循继承模型。将工作视为由特定客户完成/工作的工作。因此,在这种情况下,客户将是一个超级类,其中Job(让我们称之为客户工作)是一个子类,因为如果没有客户,Job就不能存在。客户还可以拥有一份工作列表,主要是为了便于数据获取。直觉上这是没有意义的,因为工作和客户完成似乎有任何关系,但是一旦你看到没有客户就不能存在工作,它只是成为客户的延伸。
-1
2017-11-08 10:00