(10)python中的类
【1】面向对象的概念及核心特性
面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
【2】类的定义与实例化(类本身就体现了封装特性)
【2.1】类的定义与调用
# 类的定义 # class Role: 经典类 class Role(object): def __init__(self, name, role, weapon, life_value=100, money=15000): # 类的构造函数(初始化函数) self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah...,I got shot...") def buy_gun(self, gun_name): print("just bought %s" % gun_name) # 类的调用 r1 = Role('Alex', 'police', 'AK47') # 实例化(又可以成为,初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化,相当于 Role('r1','Alex', 'police', 'AK47') r2 = Role('Jack', 'terrorist', 'B22') # 生成一个角色 r1.shot() r1.buy_gun('AK47')
【2.2】类的实例化
# 类的调用 r1 = Role('Alex', 'police', 'AK47')
实例化:(又可以称为初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化
【2.3】类的实例化原理
案例:
class Dog(object): print("hello,I am a dog!") d = Dog() #实例化这个类, #此时的d就是类Dog的实例化对象 #实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名
上面的代码其实有问题,想给狗起名字传不进去。
class Dog(object): def __init__(self,name,dog_type): self.name = name self.type = dog_type def sayhi(self): print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象 d.sayhi()
为什么有__init__? 为什么有self? 此时的你一脸蒙逼,相信不画个图,你的智商是理解不了的!
画图之前, 你先注释掉这两句
# d = Dog('LiChuang', "京巴") # d.sayhi() print(Dog)
没实例直接打印Dog输出如下
<class '__main__.Dog'>
这代表什么?代表 即使不实例化,这个Dog类本身也是已经存在内存里的对不对, yes, cool,那实例化时,会产生什么化学反应呢?
那么,这个图,只是初始化时,把对象的相关属性给存储到了每个对象自己独立的空间里去了。
如果我要调用 sayhi方法该怎么办?sayhi是存储在哪里?
实际上,类本身也是存储在内存的,会开辟一个内存空间的,在对象的空间中会存储自己属于哪一个类,会存储自己所在类的地址,那么当要调用类中方法时,就会通过这个地址找到这个类所在的地址,然后找到对应方法;
【3】实例变量与类变量
【3.1】基本形式
类变量:其作用于一般就是给一些通用的默认值(比如我绝大部分用户国籍是中国,那么我可以定义一个country = 'china' ,如果是外国用户,就用实例变量)
实例变量:就是该对象的属性,那如果该用户是俄罗斯的,那么我们可以 d=user('俄罗斯伙计') ,生成实例变量 d.country = 'russia',生成实例变量后,就会使用实例变量,而非类变量,这样该用户就有他自己独特的属性了;
class Dog(object): n = 123 # 类变量,作用域是整个类及类的对象
name = 'a' def __init__(self,name,dog_type): self.name = name # 实例变量(静态属性),作用域就是实例本身 self.type = dog_type def sayhi(self): # 类的方法,功能(动态属性) print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象,也可以称之为 d 是 dog 这个类的实例 d.sayhi() # 调用类的方法 print(d.name) # 调用类的变量
print(d.n)
输出结果:
hello,I am a dog, my name is LiChuang LiChuang
123
由此可知:
我们上面有2个name,但打印输出的却是实例变量
调用规则:
实例 ==》先找实例存储空间本身(比如 d.name) =》找不到之后再去找类中的变量(比如 d.n);
【3.2】实例属性的增删查改
class Dog(object): n = 123 # 类变量,作用域是整个类及类的对象 name = 'a' def __init__(self,name,dog_type): self.name = name # 实例变量(静态属性),作用域就是实例本身 self.type = dog_type def sayhi(self): # 类的方法,功能(动态属性) print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象,也可以称之为 d 是 dog 这个类的实例
d.dao_age = 3 # 增加/修改:如果 dao_age 属性不存在,则增加,如果存在则修改
print(d.dao_age) # 查询
del d.name # 删除:name 属性是已经存在的,我们可以在该实例自己的空间中删掉该属性
d.dao_age = 4
print(d.dao_age,d.name)
输出:
3 4 a # d.name 的实例变量被删除了,LiChuang就不存在了,后面输出 a 是因为调用的是类变量 name
实例无法增删改类常规类型变量,在上面删掉 d.name 之后,d 实例本身的实例变量 name 已经被删除
且输出的已经是 类的 name值,这样我们再次删除调用的是类的 name,删除操作如下,结果报错,不行的
如果类变量是列表、元组、集合等的话,那么就可以被改变,如下:
【4】类的 构造函数 与 析构函数
(1)构造函数(__init__):在实例生成时自动执行,它初始化赋值函数,即我们前面类定义 的 def __init__(...)
(2)析构函数(__del__):在实例释放、销毁(比如程序执行完毕、手动del)的时候自动执行,通常用于做一些收尾工作,比如:关闭一些数据库连接,关闭临时文件等等
在生成实例对象时,每一个实例对象都会运行 构造函数 在程序执行完毕后,每一个实例对象都会运行 析构函数
例子:
# 类的定义 class Role(object): def __init__(self, name, role, weapon, life_value=100, money=15000): # 类的构造函数(初始化函数) self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah...,%s got shot..." % self.name) def buy_gun(self, gun_name): print("%s just bought %s" % (self.name,gun_name)) def __del__(self): # 类的 析构函数 print('{} 已经彻底死了'.format(self.name)) # 类的调用 r1 = Role('Alex', 'police', 'AK47') # 实例化(又可以成为,初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化,相当于 Role('r1','Alex', 'police', 'AK47') r2 = Role('Jack', 'terrorist', 'B22') # 生成一个角色 r1.shot() r1.buy_gun('AK47') r1.buy_gun('AK47') r1.got_shot()
执行结果如下,结果表示,在生成实例时,每一个实例对象都会运行 构造函数 在程序执行完毕后,每一个实例对象都会运行 析构函数
Alex just bought AK47
ah...,Alex got shot...
Alex 已经彻底死了
Jack 已经彻底死了
【5】私有属性(属性名称前面加__,比如 self.__name = name)和公有属性
(私有属性 与 私有方法)
概念:就是不让外部使用,只能在类的内部方法才能调用这些属性。私有方法也是如此
形式:在属性或者方法前面加__ ,2个下划线
比如 self.__name = name 或者 def __shot():
具体案例参考下图:
(1)私有属性
如上图,我们用类中的函数调用私有属性,则成功显示,用实例直接调用属性,失败!而且,如果你在书写的时候用实例调用私有方法、函数,那么连自动提示都会消失
【6】继承
【6.1】继承的定义(新式类【推荐】、经典类)
新式类和经典类的区别是什么? 核心区别就是新式类多类继承时的访问顺序;
# class People:经典类 class People(object): # 新式类定义 def __init__(self, name, age, ): self.name = name self.age = age def eat(self): print("{} is eating...".format(self.name)) def talk(self): print("{} is talking...".format(self.name)) def sleep(self): print("{} is sleeping...".format(self.name)) class Man(People): # 类的继承 ,可以继承多个类 class Man(People,animal) def __init__(self, name, age, money): # 子类 重构构造方法 super(Man, self).__init__(name, age) # 新式类的构造继承调用 建议使用这种方式,如果2个类参数相同就可以省事,且如果修改了继承类的代码,这块也不用改 # People(self,name,age) # 经典类的构造继承调用 self.money = money def sleep(self): # 重构父类方法 People.sleep(self) # 新式类写法: supper(main,self).sleep() print("Man is sleeping..") def man_strong(self): # 子类自己额外的方法 print("Man's Strong is power") class Woman(People): def get_birth(self): print("{} is born a boby...".format(self.name)) m1 = Man("张三", 22, 10) m1.eat() w1 = Woman("李娟", 26) w1.get_birth()
前置信息:
dad,mom
class son(dad,mom) 继承了2个类
调用关系:
(1)当实例化 son时=》如果 son 没有构造函数
=》当继承了多个父类,则son(dad,mom) 会从左到右,先访问使用 dad 的 构造函数(找到第一个 构造函数就会停下来)
=》如果 dad 也没有构造函数,则访问 mom的构造函数
=》如果 mom 也没有构造函数,则访问 grandpa
(2)当实例化 son时=》如果 son 有构造函数=》则直接使用 son的构造函数
【6.2】继承的概念与实例
继承的概念:
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
class School(object): def __init__(self, name, addr): self.name = name self.addr = addr self.student = [] self.teacher = [] self.staffs = [] def enroll(self,stu_obj): print("为学生 {} 办理登记入学手续!".format(stu_obj.name)) self.student.append(stu_obj) def hire(self,staff_obj): print("为教师 {0} 办理入职手续!".format(staff_obj.name)) self.staffs.append(staff_obj) class SchoolMember(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def tell(self): # 信息获取 pass class Teacher(SchoolMember): def __init__(self,name,age,sex,salary,course): super(Teacher,self).__init__(name,age,sex) self.salary = salary self.course = course def tell(self): print(''' ----- info of student {name} ----- Name = {name} Age = {age} Sex = {sex} Salary = {salary} Course = {course} '''.format(name=self.name, age=self.age, sex=self.sex, salary=self.salary,course=self.course)) def teach(self): print("{name} is teaching course[{course}]".format(self.name,self.course)) class Student(SchoolMember): def __init__(self, name, age, sex, stu_id, grade): super(Student, self).__init__(name, age, sex) self.stu_id = stu_id self.grade = grade def tell(self): print(''' ----- info of student {name} ----- Name = {name} Age = {age} Sex = {sex} Stu_id = {stu_id} Grade = {grade} '''.format(name=self.name, age=self.age, sex=self.sex, stu_id=self.stu_id,grade=self.grade)) def pay_tuition(self,amount): print("{} has pay tution learn ${} ".format(self.name,amount)) school = School("第二中学","市中心") t1 = Teacher("张三",56,"MF",200000,"Linux") t2 = Teacher("李四",22,"M",3000,"PythonDevOps") s1 = Student("小王",36,"MF",1001,"PythonDevOps") s2 = Student("小李",23,"MF",1002,"linux") s1.tell() s2.tell() school.hire(t1) school.hire(t2) school.enroll(s1) school.enroll(s2) for stu in school.student: stu.pay_tuition(5000)
执行结果:
----- info of student 小王 ----- Name = 小王 Age = 36 Sex = MF Stu_id = 1001 Grade = PythonDevOps ----- info of student 小李 ----- Name = 小李 Age = 23 Sex = MF Stu_id = 1002 Grade = linux 为教师 张三 办理入职手续! 为教师 李四 办理入职手续! 为学生 小王 办理登记入学手续! 为学生 小李 办理登记入学手续! 小王 has pay tution learn $5000 小李 has pay tution learn $5000
【6.3】组合:把对象值赋给属性,达到多继承效果
有 school 类
【7】多态
class Animal(object): def __init__(self,name): self.name = name def talk(obj): obj.talk() class Dog(Animal): def talk(self): print("dog :{} ,woof woof !".format(self.name)) class Cat(Animal): def talk(self): print("cat :{} ,meow meow !".format(self.name)) d = Dog("旺财") c = Cat("大喵") d.talk() # 常规 c.talk() Animal.talk(d) # 多台,一个接口多个用法 Animal.talk(c)
输出:
dog :旺财 ,woof woof !
cat :大喵 ,meow meow !
dog :旺财 ,woof woof !
cat :大喵 ,meow meow !
【8】静态方法、类方法、属性方法
【8.1】静态方法
静态方法: 只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性
class Dog(object): def __init__(self,name): self.name = name @staticmethod # 静态方法,实际上跟类没什么关联了,它就和普通的函数一样了 def eat(self,food): print("{} eating {}".format(self.name,food)) d = Dog("旺财") # 使用静态方法后,就完全像函数一样了,只能像下面这2种情况调用 Dog.eat(d,"包子") d.eat(d,"骨头")
输出:
旺财 eating 包子
旺财 eating 骨头
【8.2】类方法
只能访问类变量,不能访问实例变量
# -*- coding: utf8 -*- class Dog(object): name = "类变量_旺财" def __init__(self, name): self.name = name self.__drink = None @classmethod # 类方法:方法中的参数只能使用 类变量 def talk(cls): print("the dog: {} is woof woof!".format(cls.name)) d = Dog("旺财") # 类方法调用 d.talk()
结果:
the dog: 类变量_旺财 is woof woof!
【8.3】属性方法
把一个方法变成实例属性,把实现细节隐藏了,对用户来说就是一步直接调用
class Dog(object): name = "类变量_旺财" def __init__(self, name): self.name = name self.__drink = None @property # 属性方法:把一个方法变成一个静态属性 def dog_drink(self): print("dog : {} is drinking {}".format(self.name,self.__drink)) @dog_drink.setter # 属性方法=>赋值,用了就可以 d.dog_drink = "water" def dog_drink(self,drink): print("set drink {} !".format(drink)) self.__drink = drink @dog_drink.deleter # 属性方法=>删除,用了就可以 del d.dog_drink def dog_drink(self): del self.__drink print("已经删掉了 self.__drink") d = Dog("旺财") # 属性方法调用: 就是把方法当成 静态属性 d.dog_drink # 属性方法,直接调用 d.dog_drink = "water" # 赋值方法:赋值 d.dog_drink # 属性方法,直接调用 del d.dog_drink # 属性方法:删除操作
【属性方法的实际应用场景案例】
比如,查询航空公司 班机的状态
class Flight(object): def __init__(self,name): self.flight_name = name def checking_status(self): print("checking flight %s status " % self.flight_name) return 1 @property def flight_status(self): status = self.checking_status() if status == 0 : print("flight got canceled...") elif status == 1 : print("flight is arrived...") elif status == 2: print("flight has departured already...") else: print("cannot confirm the flight status...,please check later") @flight_status.setter #修改 def flight_status(self,status): status_dic = { 0 : "canceled", 1 :"arrived", 2 : "departured" } print("\033[31;1mHas changed the flight status to \033[0m",status_dic.get(status) ) @flight_status.deleter #删除 def flight_status(self): print("status got removed...") f = Flight("CA980") f.flight_status f.flight_status = 2 #触发@flight_status.setter del f.flight_status #触发@flight_status.deleter
【9】类的特殊成员方法
1. __doc__ 表示类的描述信息
1
2
3
4
5
6
7
8
|
class Foo: """ 描述类信息,这是用于看片的神奇 """ def func( self ): pass print Foo.__doc__ #输出:类的描述信息 |
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
class C: def __init__(self): self.name = 'wupeiqi'
from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
3. __init__ 构造方法,通过类创建对象时,自动触发执行。
4.__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
5. __call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1
2
3
4
5
6
7
8
9
10
11
12
|
class Foo: def __init__( self ): pass def __call__( self , * args, * * kwargs): print '__call__' obj = Foo() # 执行 __init__ obj() # 执行 __call__ |
6. __dict__ 查看类或对象中的所有成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class Province: country = 'China' def __init__( self , name, count): self .name = name self .count = count def func( self , * args, * * kwargs): print 'func' # 获取类的成员,即:静态字段、方法、 print Province.__dict__ # 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} obj1 = Province( 'HeBei' , 10000 ) print obj1.__dict__ # 获取 对象obj1 的成员 # 输出:{'count': 10000, 'name': 'HeBei'} obj2 = Province( 'HeNan' , 3888 ) print obj2.__dict__ # 获取 对象obj1 的成员 # 输出:{'count': 3888, 'name': 'HeNan'} |
7.__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
如果不写,默认会把所有__init__中的外部传入的入参以元组形式打印出来,如果只有一个参数则输出形式为字符串
1
2
3
4
5
6
7
8
9
|
class Foo: def __str__( self ): return 'alex li' obj = Foo() print obj # 输出:alex li |
8.__getitem__、__setitem__、__delitem__
用于索引操作,如字典、列表。下面分别表示获取、设置、删除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Foo( object ): def __getitem__( self , key): print ( '__getitem__' ,key) def __setitem__( self , key, value): print ( '__setitem__' ,key,value) def __delitem__( self , key): print ( '__delitem__' ,key) obj = Foo() result = obj[ 'k1' ] # 自动触发执行 __getitem__ obj[ 'k2' ] = 'alex' # 自动触发执行 __setitem__ del obj[ 'k1' ] |
9. __new__ \ __metaclass__
1
2
3
4
5
6
7
8
|
class Foo( object ): def __init__( self ,name): self .name = name f = Foo( "alex" ) |
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
1
2
|
print type (f) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print type (Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建 |
所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
a). 普通方式
1
2
3
4
|
class Foo( object ): def func( self ): print 'hello alex' |
b). 特殊方式
1
2
3
4
5
6
7
|
def func( self ): print 'hello wupeiqi' Foo = type ( 'Foo' ,( object ,), { 'func' : func}) #type第一个参数:类名 #type第二个参数:当前类的基类 #type第三个参数:类的成员 |
# 加上构造方法
def func(self): print("hello %s"%self.name) def __init__(self,name,age): self.name = name self.age = age Foo = type('Foo',(object,),{'func':func,'__init__':__init__}) f = Foo("jack",22) f.func()
So ,孩子记住,类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
1 class MyType(type): 2 def __init__(self,*args,**kwargs): 3 4 print("Mytype __init__",*args,**kwargs) 5 6 def __call__(self, *args, **kwargs): 7 print("Mytype __call__", *args, **kwargs) 8 obj = self.__new__(self) 9 print("obj ",obj,*args, **kwargs) 10 print(self) 11 self.__init__(obj,*args, **kwargs) 12 return obj 13 14 def __new__(cls, *args, **kwargs): 15 print("Mytype __new__",*args,**kwargs) 16 return type.__new__(cls, *args, **kwargs) 17 18 print('here...') 19 class Foo(object,metaclass=MyType): 20 21 22 def __init__(self,name): 23 self.name = name 24 25 print("Foo __init__") 26 27 def __new__(cls, *args, **kwargs): 28 print("Foo __new__",cls, *args, **kwargs) 29 return object.__new__(cls) 30 31 f = Foo("Alex") 32 print("f",f) 33 print("fname",f.name) 自定义元类
类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__
metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好
【领域模型 】
好了,你现在会了面向对象的各种语法了, 那请看下本章最后的作业需求,我相信你可能是蒙蔽的, 很多同学都是学会了面向对象的语法,却依然写不出面向对象的程序,原因是什么呢?原因就是因为你还没掌握一门面向对象设计利器, 你说我读书少别骗我, 什么利器?
答案就是:领域建模。 从领域模型开始,我们就开始了面向对象的分析和设计过程,可以说,领域模型是完成从需求分析到面向 对象设计的一座桥梁。
领域模型,顾名思义,就是需求所涉及的领域的一个建模,更通俗的讲法是业务模型。 参考百度百科(http://baike.baidu.cn/view/757895.htm ),领域模型定义如下:
从这个定义我们可以看出,领域模型有两个主要的作用:
- 发掘重要的业务领域概念
- 建立业务领域概念之间的关系
【领域建模三字经 】
领域模型如此重要,很多同学可能会认为领域建模很复杂,需要很高的技巧。然而事实上领域建模非常简 单,简单得有点难以让人相信,领域建模的方法概括一下就是“找名词”! 许多同学看到这个方法后估计都会笑出来:太假了吧,这么简单,找个初中生都会啊,那我们公司那些分 析师和设计师还有什么用哦?
分析师和设计师当然有用,后面我们会看到,即使是简单的找名词这样的操作,也涉及到分析和提炼,而 不是简单的摘取出来就可,这种情况下分析师和设计师的经验和技能就能够派上用场了。但领域模型分析 也确实相对简单,即使没有丰富的经验和高超的技巧,至少也能完成一个能用的领域模型。
虽然我们说“找名词”很简单,但一个关键的问题还没有说明:从哪里找? 如果你还记得领域模型是“需求到面向对象的桥梁”,那么你肯定一下子就能想到:从需求模型中找,具 体来说就是从用例中找。
归纳一下域建模的方法就是“从用例中找名词”。 当然,找到名词后,为了能够更加符合面向对象的要求和特点,我们还需要对这些名词进一步完善,这就 是接下来的步骤:加属性,连关系!
最后我们总结出领域建模的三字经方法:找名词、加属性、连关系。
【找名词】
who : 学员、讲师、管理员
用例:
1. 管理员 创建了 北京 和 上海 两个校区
2. 管理员 创建了 Linux \ Python \ Go 3个课程
3. 管理员 创建了 北京校区的Python 16期, Go开发第一期,和上海校区的Linux 36期 班级
4. 管理员 创建了 北京校区的 学员 小晴 ,并将其 分配 在了 班级 python 16期
5. 管理员 创建了 讲师 Alex , 并将其分配 给了 班级 python 16期 和全栈脱产5期
6. 讲师 Alex 创建 了一条 python 16期的 上课纪录 Day6
7. 讲师 Alex 为Day6这节课 所有的学员 批了作业 ,小晴得了A, 李磊得了C-, 严帅得了B
8. 学员小晴 在 python 16 的 day6里 提交了作业
9. 学员李磊 查看了自己所报的所有课程
10 学员 李磊 在 查看了 自己在 py16期 的 成绩列表 ,然后自杀了
11. 学员小晴 跟 讲师 Alex 表白了
名词列表:
管理员、校区、课程、班级、上课纪录、作业、成绩、讲师、学员
【加属性】
【连关系 】
有了类,也有了属性,接下来自然就是找出它们的关系了。
【作业练习题】
角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师
5. 创建学员时,选择学校,关联班级
5. 创建讲师角色时要关联学校,
6. 提供两个角色接口
6.1 学员视图, 可以注册, 交学费, 选择班级,
6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里
【参考文档】
本系列学习笔记都转自:https://www.cnblogs.com/alex3714/articles/5188179.html 系列文档
# class People