python设计模式-UML类图中的结构及python实现
前言
最近在看设计模式。在学习初期一直有一个问题困扰着我,那就是UML类图
中的各种结构用python代码是如何实现的?这个在初期可以说是深深的困扰着我的设计模式
学习之路。下面我将介绍一下我自己总结的实现,如果有错误也希望各位给指出,不胜感激。
工具介绍
本文的UML图采用Enterprise Architect绘制。
Python版本2.7.12
(在用了Enterprise Architect 后才知道他有从类图导出代码的功能,/捂脸
早知道就用这个学习各种结构的实现了。文末有介绍如何用Enterprise Architect 导出代码。)
类图
类图是由类和类之间的连接关系构成。
类我们这里只介绍类和抽象类。
连接关系我们介绍泛化(generalization)、实现(realize)、聚合(aggregation)、组合(compositon)、关联(assocation)和依赖(dependency)共六种。
类
抽象类(java中接口)
图示
接口在UML中的图形为
可以看到再其上半部分有一个interface的表示。在接口中一般只给出接口的定义
而实现一般放在子类和实现。
代码
在python中本事是没有接口和抽象类这一概念的,但是可以通过abc(Abstract Base Class)这个python库来实现。或者是通过抛出NotImplementedError这个异常来实现。
通过abc模块来实现
from abc import ABCMeta, abstractmethod, abstractproperty class Drawable: __metaclass__ = ABCMeta @abstractproperty def size(self): pass @abstractmethod def draw(self, x, y, scale=1.0): pass class Cicle(Drawable): def size(self): print 'Cicle' def draw(self, x, y, scale=1.0): print str(x*y*scale) c = Cicle()
如果在子类里面没有实现方法,当实例化子类的时候会报错。abc模块的具体使用这里就不介绍了。放上abc的 PEP3119
通过抛异常来实现
class Drawable1: def size(self): raise NotImplementedError def draw(self, x, y, scale=1.0): raise NotImplementedError class Cicle(Drawable): def size(self): print 'Cicle' def draw(self, x, y, scale=1.0): print str(x*y*scale) c = Cicle() c.draw()
这里如果子类没有实现方法,在实例化的时候不会报错。只有当调用到未实现的方法时候才会报错。这种通过abc的注册机制也可以实现。
类
图示
类的图示如下
可以看到整个图形分为三部分:上面为类名,中间为类属性,下面为类方法。
其中可以看到有- + 和# 三种符号,其分别代表私有、公有、保护。其中保护变量在python中是不存在的。
公有变量可以在类外被直接访问。且可以被子类继承,私有变量只能再此类中被访问且不可以被子类继承。
代码
class Flower(object): def __init__(self, floral=None, leaf=None): self.floral = floral self.__leaf = leaf def flowing(self): print 'flower' def __grow(self): print 'grow grow'
其中以两条下划线开头的变量为私有变量,方法为私有方法。
其实在python中也不是在类外面无法访问到私有变量或方法。只是在生成python字节码的时候在编译器自动再含有__ 开头的属性或变量前加上了 _{classname}了你可以在类外通过 _Flower__leaf 访问到__leaf属性,当然这并没有什么意义。
连接关系
泛化(generalization)
图示
首先我们来看一下泛化关系的UML结构图。
连接器的是由一条直线和一个三角组成的,连接的两端都是类。在代码中的结构就是继承非抽象类。
代码
class Car(object): def __init__(self): self.wheel = ['11', '12', '21', '22'] self.body = 'car body' def run(self): print self.wheel print self.body def horn(self): print 'bbbbbbb' class SUV(Car): def run(self): print 'suv' super(SUV, self).run() class Jeep(Car): def run(self): print 'jeep' super(Jeep, self).run() jeep = Jeep() jeep.run() jeep.horn()
我们可以看到Jeep和SUV继承了Car这个非抽象类
实现(realize)
图示
从图中可以看出实现关系为一个三角箭头加一段虚线构成。箭头指向抽象类。
在代码中表现为继承抽象类。
代码
class Vehicle(object): def run(self): raise NotImplementedError class Car(Vehicle): def run(self): print 'car run run' class Bicycle(Vehicle): def run(self): print 'bicycle run run'
代码中Car和Bicycle继承了Vehicle这个抽象类。
关联关系(assocation)
关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;
比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;[1]
图示
关联关系为一条直线,可以有箭头,如图表示car知道Moter的存在而Moter不知道Car的存在。表现在代码中就是Car中有一个属性为Motor类的实例。也可以为双箭头表示互相知道。
代码
class Motor(object): pass class Car(object): def __init__(self): self.motor = Motor()
依赖关系(dependency)
他描述一个对象在运行期间会用到另一个对象的关系.与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;
显然,依赖也有方向,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;
注:在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还是“使用”对方的方法和属性;[1]
图示
从图中可以看出依赖关系为在类的方法中将另一个类当作参数传入。
代码
class People(object): def cooking(self, wok): wok.cook() class Wok(object): def cook(self): print 'cook'
我们在People的cooking方法中传入了Wok实例,调用其cook方法,完成people的cooking动作。
组合关系(composition)
组合关系表示整体由部分构成,但是当整体不存在时部分也不存在,是一种强依赖关系。
图示
从图中看到组合关系是由一个实心的菱形箭头表示,菱形箭头指向整体。 公司由部分组成。当公司不存在了,部门也就不存在了。
代码
class Company(object): def __init__(self): self.__departments = [] def build_department(self, department): self.__departments.append(department) class Department(object): def __init__(self, name): self.name = name c = Company() d1 = Department('1') d2 = Department('2') c.build_department(d1) c.build_department(d2)
聚合关系(aggregation)
聚合关系表示整体由部分构成但是当整体不存在的时候部分也是可以存在的。
图示
从图中看到聚合关系由一个空心的菱形箭头表示, 菱形箭头指向整体。及公司是由人组成的。 当公司不存在的时候人还是可以单独存在的。 组合关系和聚合关系由常识来区别的。在实现上区别不大。
代码
class Company(object): def __init__(self): self.__employees = [] def add_employee(self, people): self.__employees.append(people) class People(object): pass c = Company() p1 = People() p2 = People() p3 = People() c.add_employee(p1) c.add_employee(p2) c.add_employee(p3)
如何从Enterprise Architect 导出代码
有点长我把这个再写一篇,
如何从Enterprise Architect 导出代码