Ahm*_*lag 2 python oop uml class-diagram bidirectional
在 Martin Fowler 的 UML Distilled 的“双向关联”部分,他说:
在编程语言中实现双向关联通常有点棘手,因为您必须确保两个属性保持同步。使用 C#,我使用以下代码来实现双向关联:
书中的代码
class Car...
public Person Owner {
get {return _owner;}
set {
if (_owner != null) _owner.friendCars().Remove(this);
_owner = value;
if (_owner != null) _owner.friendCars().Add(this);
}
}
private Person _owner;
...
class Person ...
public IList Cars {
get {return ArrayList.ReadOnly(_cars);}
}
public void AddCar(Car arg) {
arg.Owner = this;
}
private IList _cars = new ArrayList();
internal IList friendCars() {
//should only be used by Car.Owner
return _cars;
}
....
Run Code Online (Sandbox Code Playgroud)
问题 1:
我尝试在python中实现这个(get_cars()
在Person
&&get_owner_v2()
在Car中),我想知道我的代码是否可以用来描述“双向关联”,如果不能,应该如何修改它?
注意:第一个版本(检查调用者的类/对象)运行良好,直到我开始独立创建汽车对象并分两步将它们分配给所有者(最后 4 个打印语句证明了这一点)。第二个版本使用许可证号lno
来找出所有者。不确定我是否做对了,但根据我的理解,它确实有效。
我的实现:
#trying the inverse-bidirectional association
class Person:
cars_and_lnos = []
def __init__(self,name):
self.__cars = []
self.__cars.append("Dummy")
self.__name = name
def add_car(self, *args, obj = None):
# import inspect
if not obj:
car_object = Car(*args)
else:
car_object = obj
Person.cars_and_lnos.append((car_object,self.__name))
self.__cars.append(car_object)
def __repr__(self):
return f"{self.__name}"
def get_cars(self):
return self.__cars
class Car:
car_count = 0
def __init__(self, lno, price ,year, make, model):
import inspect
self.__lno = lno
self.__price = price
self.__year = year
self.__make = make
self.__model = model
Car.car_count += 1
self.__car_id = Car.car_count
if "self" in inspect.getargvalues(inspect.stack()[1][0]).args :
self.__owned_by = f"Car (ID:{self.__car_id}) is Owned By: {inspect.stack()[1][0].f_locals['self']}, which is an instance of Class: {inspect.stack()[1][0].f_locals['self'].__class__.__name__}"
else:
self.__owned_by = "This car is not owned by anyone."
def __repr__(self):
return f"Car ID: {self.__car_id}."
def get_specs(self):
print(f"+{'-'*30}+")
print(f"""
Liscense No.: {self.__lno}
Price: {self.__price}
Year: {self.__year}
Make: {self.__make}
Model: {self.__model}
""")
@property
def get_lno(self):
return self.__lno
def get_owner_v1(self):
# import inspect
return self.__owned_by
def get_owner_v2(self):
if Person.cars_and_lnos:
for tup in Person.cars_and_lnos:
if self.__lno == tup[0].get_lno:
return f"Car (ID: {self.__car_id}) is owned by: {tup[1]}, he is a: {Person.__name__} Class."
return "[0] This car is not owned by anyone."
else:
return "[1] This car is not owned by anyone."
owner1 = Person("William")
owner1.add_car("4567781",10000,2012,"Toyota","Corrolla")
owner1.add_car("2137813",8000,2010,"Porshe","GT3")
owner1.get_cars()[1].get_owner_v1()
print(f"{owner1} owns {len(owner1.get_cars())-1} Car(s).")
print(owner1.get_cars()[1].get_owner_v1())
print("=====================================================")
owner2 = Person("Defoe")
owner2.add_car("8729120",10000,2012,"Dodge","Challenger")
print(f"{owner2} owns {len(owner2.get_cars())-1} Car(s).")
print(owner2.get_cars()[1].get_owner_v1())
print("=====================================================")
car1 = Car("7839291",10000,2012,"Chevrolet","Camaro")
car2 = Car("6271531",10000,2012,"Ford","Mustang")
print(car2.get_owner_v1())
print("=====================================================")
owner3 = Person("Lyan")
owner3.add_car("656721",9000,2013,"Toyota", "Camry")
owner3.add_car("652901",9000,2013,"Nissan", "Sunny")
owner3.add_car("870251",9000,2013,"BMW", "6 Series")
print(owner3.get_cars()[1].get_owner_v2())
print(owner2.get_cars()[1].get_owner_v2())
print(owner1.get_cars()[1].get_owner_v2())
print("=====================================================")
car3 = Car("5424201",10000,2012,"Volks","Eos")
print(car3.get_owner_v1())
print(car3.get_owner_v2())
owner4 = Person("Daphne")
owner4.add_car(obj=car3)
print(car3.get_owner_v1())
print(car3.get_owner_v2())
Run Code Online (Sandbox Code Playgroud)
问题2:
在本节中,他说这两个符号可能相同:
下面的符号(没有指定任何箭头)是否也可以被认为是双向关系?
编辑:
我知道对类图的逻辑改进将是 * 到 *(多对多),但我只关心我对描述/设计的实现有多正确,如何改进,以及哪里不 -箭头关联线适合图片。
问题2已经完美回答了。因此,在关注问题 1 之前,我将仅提供一些有关适航性的补充信息。
可导航性是在 UML 规范中以非常广泛的术语定义的:
可导航性意味着可以从关联另一端的实例有效地访问运行时参与链接的实例(关联的实例)。实现这种高效访问的精确机制是特定于实现的。如果一端不可导航,则从另一端访问可能会也可能不会,如果是,则可能效率不高。
可导航性表达了有关实现的一些承诺或约束。因此,在早期阶段通常不指定可航行性。在后面的阶段,您将明确显示必须支持的导航路径。但很少有系统地完成,而且很少看到不可导航性(X 而不是箭头)。
结论:当你看到没有箭头和 X 时,你不能得出任何结论,除非你的团队已经定义了一些对这种情况更精确的约定。
关联可以通过多种方式实现。典型的实现是在类 A 的对象中保留对类 B 的关联对象的一个或多个引用。根据定义,这确保了可导航性,A ---> B
因为存在有效访问。但是回来的路呢?
如果您什么都不做,对象 B 就无法轻松找到与其关联的对象 A。如果引用不是私有的(这将使搜索变得不可能),唯一的方法是遍历所有 A 对象以找到其引用(可能但效率不高)。所以你会有一条不可导航的返回方式 A x--> B
为了使其可双向导航,您将在 B 对象中保留对关联 A 对象的引用。这使得双向通航:A <--> B
。
这里的主要挑战是您必须保持两个对象中的引用同步。
Person
A 保留了已拥有的Car
s C1、C2 和 C3的列表,那么互惠地每个Car
C1、C2、C3 将保留对其所有者 A 的引用。设计问题:每个类都应该被很好地封装并使用另一个类的公共访问。所以Owner.AddCar(Car)
并且Car.SetOwner(Owner)
应该在公共接口中公开。但是,如果您在某处调用B.AddCar(C4)
,该AddCar()
方法将需要调用C4.SetOwner(B)
以确保一致性。但是SetOwner()
方法也可以从外部调用,因此也希望通过调用来保持同步B.AddCar(C4)
,然后......你会以两个方法的堆栈溢出结束,它们永远相互调用以保持同步。
Fowler 的代码通过授予Car
对Person
通过friendCars()
. 这解决了问题,但需要Car
了解内部结构Person
并创建不必要的耦合。
我不是 Python 专家,但据我所知,您的代码是不同的:
cars_and_lnos
,其中包含所有汽车及其所有者的元组。__owned_by
仅在汽车制造时更新,之后不会更新。因此,在基于 的添加过程中构建汽车时,它可能会起作用*arg
,但在将现有汽车添加到所有者时会失败。get_owner_v1()
失败,因为它基于__owned_by
. 但 get_owner_v2()
效果很好,因为它迭代(非常低效)cars_and_lnos
哪个是最新的。简而言之,v1 失败是因为您的代码产生了不一致的对象。
话虽如此,您的方法比 Fowler 的方法更有前途:
cars_and_lnos
进入一个单独的类CarRegistration
,并根据精心设计的api重写Car
并Person
与之交互CarRegistration
以更新所有权。CarRegistration
来替换 cars_and_lnos
优化搜索。 归档时间: |
|
查看次数: |
556 次 |
最近记录: |