结构化与面向对象(下)

2 面向对象方法

儿子从二年级开始就一直在学Python,这几天跟我说想用Python编写类似“我的世界”的游戏,而且还兴致勃勃的说,它准备设计一个配置文件,游戏开始时,程序需要根据配置文件绘制游戏场景,场景中会包含基岩、草方块等。我们就以这个“从配置文件读取配置并绘制场景”的问题出发,来看看如何从结构化过渡到面向对象。

儿子设计的配置文件(假设文件名为:scene.cfg)的内容大概是下面这个样子:

1000,511
10,10,基岩

100,200,草方块

......

其中第一行为场景的宽和高,从第2行开始,每行代表1个目标,格式为“x坐标,y坐标,目标名称”。

我们先来看看结构化的方法,也就是单纯采用函数的方式如何实现这样一个功能,下面给出粗略的代码:

#全局变量:场景宽度
width = 0
#全局变量:场景高度
height = 0
#全局变量:游戏目标列表,列表中每个游戏目标用一个三元组表示,即(x坐标,y坐标,目标名称)。
lstObject = []
###函数:加载游戏场景
###输入:filename-配置文件名
###输出:True-读取成功,否则读取失败
def loadScene(filename):
    #判断配置文件是否存在
    if os.path.exists(filename):
        try:
            #打开配置文件
            with open(filename, encoding='UTF-8') as f:
                #初始化计数器i,用于记录读取到的信息行数
                i = 0
                #循环读取文件
                while True:
                    #从文件中读取一行数据到变量s
                    s = f.readline()
                    #如果读取到了有效的信息则进行相关处理
                    if s != None and s != '':
                        #以英文逗号分隔读取到的信息,并记录到变量l(列表)
                        l = s.split(',')
                        #如果得到有效的分割信息,则进行相关处理
                        if l != None:
                            #如果i=0,则读取到的是场景大小信息
                            if i == 0:
                                #如果信息有效,则把读取到的数据分别计入width和height
                                if len(l) > 1:
                                    width = int(l[0])
                                    height = int(l[1])
                            #否则,读取到的是游戏目标信息
                            else:
                                #如果信息有效,则把读取到的数据存入lstObject
                                if len(l) > 2:
                                    lstObject.append((int(l[0]), int(l[1]), l[2]))
                    #否则,终止读取
                    else:
                        break
                    #更新计数器
                    i = i + 1
            return True
        except:
            pass
    return False
                
#调用函数,读取游戏配置
if loadScene('scene.cfg'):
    print('读取成功')
else:
    print('读取失败')
    

那么,如果采用面向对象的方法,代码该是什么样子呢?其实,我们可以将读取配置文件以及文件中包含的游戏目标对象化,也就是把它们封装成类。下面给出大致的代码:

#导入游戏配置模块
import conf

#全局变量:场景宽度
width= 0
#全局变量:场景高度
height=0
#实例化一个配置文件对象并获取配置信息
config = conf.SceneConfig('scene.cfg')
if config.errorNo = 0:
    width = config.getWidth()
    height = config.getHeight()
    print('读取成功')
else:
    print('读取失败,错误号:', errorNo)
    

比较一下,有什么变化?首先,代码明显变短啦;其次,文件头部增加了一行“import ...”;最后,一个最明显的变化就是用到了一个名为config的对象,用于专门处理配置文件的读取。下面,我们来看看这个config对象是怎么回事儿?其实,就是在头部应用的conf模块中,我们将读取配置文件这件事儿,封装成一个名为SceneConfig的类。具体的代码如下: 

#conf.py

#场景目标类
class GameObject:
    x = 0
    y = 0
    text = None
    
    #构造函数
    def __init__(self, x, y, text):
        self.x = x
        self.y = y
        self.text = text

    #构造函数
    def __init__(self):
        self.x = 0
        self.y = 0
        self.text = None
        
#场景配置操作类
class SceneConfig:
    #场景宽度
    width = 0
    #场景高度
    height = 0
    #场景包含的目标列表
    lstObject = []
    #操作过程中的错误信息
    error = ''
    #操作过程中的错误号
    errorNo = 0

    #构造函数
    #参数:filename-配置文件名
    def __init__(self, filename):
        #判断配置文件是否存在
        if os.path.exists(filename):
            try:
                #打开配置文件
                with open(filename, encoding='UTF-8') as f:
                    #初始化计数器i,用于记录读取到的信息行数
                    i = 0
                    #循环读取文件
                    while True:
                        #从文件中读取一行数据到变量s
                        s = f.readline()
                        #如果读取到了有效的信息则进行相关处理
                        if s != None and s != '':
                            #以英文逗号分隔读取到的信息,并记录到变量l(列表)
                            l = s.split(',')
                            #如果得到有效的分割信息,则进行相关处理
                            if l != None:
                                #如果i=0,则读取到的是场景大小信息
                                if i == 0:
                                    #如果信息有效,则把读取到的数据分别计入width和height
                                    if len(l) > 1:
                                        self.width = int(l[0])
                                        self.height = int(l[1])
                                #否则,读取到的是游戏目标信息
                                else:
                                    #如果信息有效,则把读取到的数据存入lstObject
                                    if len(l) > 2:
                                        lstObject.append((int(l[0]), int(l[1]), l[2]))
                        #否则,终止读取
                        else:
                            break
                        #更新计数器
                        i = i + 1
            except Exception as e:
                self.error = e.strerror
                self.errorNo = e.errorno
    
    #获取第i个场景目标
    def getObject(self, i):
        if i >= 0 and i < len(self.lstObject):
            return self.lstObject[i]
        return None
    #获取场景目标的个数
    def getCount(self):
        return len(self.lstGameObject)
    #获取场景宽度
    def getWidth(self):
        return self.width
    #获取场景高度
    def getHeight(self):
        return self.height

那么,这样做有什么好处呢?接下来让我们一起来探讨一下。 

2.1 面向对象方法的发展历程

面向对象方法是将面向对象的思想应用于软件开发过程中,用于指导开发活动,是建立在“对象”概念基础上的方法学,简称OO(Object-Oriented)方法。

OO方法起源于面向对象的编程语言(简称为OOPL)。50年代后期,在用FORTRAN语言编写大型程序时,常出现变量名在程序不同部分发生冲突的问题。鉴于此,ALGOL语言的设计者在ALGOL60中采用了以"Begin……End"为标识的程序块,使块内变量名是局部的,以避免它们与程序中块外的同名变量相冲突。这是编程语言中首次提供封装(保护)的尝试。此后程序块结构广泛用于Pascal 、Ada、C等高级语言之中。

60年代中后期,Simula语言在ALGOL基础上研制开发,它将ALGOL的块结构概念向前发展一步,提出了对象的概念,并使用了类,也支持类继承。

70年代,Smalltalk语言诞生,它将Simula提出的类作为核心概念,其很多内容也借鉴于Lisp语言。Xerox公司经过对Smalltalk72、76持续不断的研究和改进之后,于1980年推出商品化的Smalltalk,它在系统设计中强调对象概念的统一,引入对象、类、方法、实例等概念和术语,采用动态联编和单继承机制。

从80年代起,人们基于以往提出的有关信息隐蔽和抽象数据类型等概念,以及由Modula2、Ada和Smalltalk等语言所奠定的基础,再加上客观需求的推动,进行了大量的理论研究和实践探索,不同类型的面向对象语言(如:Object-c、Eiffel、c++、Java、Object-Pascal等)如雨后春笋般的发展起来,出现了OO方法的概念理论体系和实用的软件系统。

80年代以来,面向对象方法已被广泛应用于程序设计语言、形式定义、设计方法学、操作系统、分布式系统、人工

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值