一个简单的对象模型
1、简介
面向对象编程是如今一种非常重要的编程范式,许多语言都支持某些形式的面对对象。然而事实上不同的面对对象的编程语言具有许多相似之处。大多数语言的共同之处是对象的存在和某种继承机制。然而,类并不是每种语言都直接支持的特性。比如,在基于原型的语言 Self 或 JavaScript 类的概念不存在,对象直接从彼此继承。
理解不同对象模型的区别是十分有趣的。他们经常揭示不同语言的相似之处。将新的语言模型放到其他语言模型的背景下,可以有助于更好的理解新的模型,同时对于语言的设计会有更好的感觉。
本文我们探索一系列非常简单的对象模型的实现。我们从实例和类以及类中的方法出发。这是经典的面对对象的方法,在早期的面对对象语言Simula 67和Smalltalk出现。这个模型一步步延伸,接下来两步是探索不同语言的设计选择,然后最后一步是提高对象模型的效率。最后的模型并不是真正的语言,而是Python对象模型的理想化、简化的版本。
模型的呈现会使用到Python语言,代码在2.7和3.4上都可以使用。为了理解这些设计的选择,本文也会有测试对于对象模型。Python作为实现的语言的选择是不切实际的,一个真正的虚拟机通常是在一个低层次的语言,如C\C++和许多需要注意的细节,使其成为有效的实施工程。然而,更简单的实现语言更关注实际的行为差异,而不是陷入执行细节的困境。
2、基于方法的模型
我们将要开始的对象模型是一个简单版本的Smalltalk。Smalltalk是一种面向对象的语言,是由Alan kay的团队在Xerox PARC 1970年代设计出来的,它普及了面向对象编程,是如今很多编程语言的许多特性的源头。Smalltalk语言设计的一个重要特征是“一切都是对象”、线啊应用最成功的是Ruby,它应用了一种类c的语法但是保留了大部分Samlltalk的对象模型。
本文中的对象模型具有他们的类和实例、对属性读写的能力、调用对象上方法的能力以及类作为另一个子类的能力,从一开始,类将是完全普通的对象,他们本身可以拥有属性和方法。
我们用实例表示一个不是类的对象。开始的一个好方法是编写一个测试来制定要实现的行为应该是什么。本文中提到的所以测试将由两部分组成。首先是一段普通的Python代码定义和使用一些类,并利用Python对象模型的日益高级的特性。第二,使用我们将在本文中实现的对象模型进行相应的测试,而不是普通的Python类。
使用普通Python类和使用我们的对象模型的映射我们在测试中手动完成。比如:我们在对象模型中写一个方法 obj.read_attr(“attribute”)替代python中的写法 obj.attribute。在实际语言中,这种映射是由解释器或编译器完成的。
本文的进一步简化是,我们实现了对象模型的代码和用于编写对象中使用的方法的代码中间没有明显得区别。在实际系统中,这两种语言通常用不同的编码语言实现。让我们从一个简单的读取和写入对象字段的测试开始。
def test_read_write_field():
# python code
class A(object):
pass
obj = A()
obj.a = 1
assert obj.a == 1
obj.b = 5
assert obj.a == 1
assert obj.b == 5
obj.a = 2
assert obj.a == 2
assert obj.b == 5
#object model code
A = Class(name="A", base_class = OBJECT, fields={}, metaclass=TYPE)
obj = Instance(A)
obj.write_attr("a", 1)
assert obj.read_attr("a") == 1
obj.write_attr("b", 5)
assert obj.read_attr("a") == 1
assert obj.read_attr("b") == 5
obj.write_attr("a", 2)
assert obj.read_attr("a") == 2
assert obj.read_attr("b") == 5
这个例子我们使用了三个工具,类Class、实例Instance。两种特殊的类的实例:OBJECT\TYPE,类比python中的原始基类object和类型type。使用Class和Instance的实例,通过继承基类BASE的一些方法作为共享的接口。
class Base(object):
# the base class that all of the object model classes inherit from
def __init__(self, cls, fields):
# every object has a class
self.cls = cls
self._fields = fields
def read_attr(self, fieldname):
# read field 'fieldname' out of the object
return self._read_dict(fieldname)
def write_attr(self, fieldname, value):
# write field 'fieldname' into the object
self._write_dict(fieldname)
def isinstance(self, cls):
# return ture if the object is an instance of class cls
return self.cls.issubclass(cls)
def callmethod(self, methname, *args):
# call method 'methname' with arguments 'args' on object
meth = self.cls._read_from_class(methname)
return meth(self, *args)
def _read_dict(self, fieldname):
# read an field 'fieldname' out of the object's dict
return self._fields.get(fieldname, MISSING)
def _write_dict(self, fieldname, value):
# write a field 'fieldname' into the object's dict
self._fields[fieldname] = value
MISSING = object()