35、面向对象编程中的类设计与实现

面向对象类设计与实现

面向对象编程中的类设计与实现

在面向对象编程的世界里,类和对象是构建程序的核心元素。下面我们将深入探讨类的设计、识别以及相关的编程技巧。

1. 程序输出示例

首先来看一个简单的程序输出示例,这是一个联系人管理程序的菜单界面:

Menu
---------------------------
1. Look up a contact
2. Add a new contact
3. Change an existing contact
4. Delete a contact
5. Quit the program

用户可以根据菜单提示输入相应的选项来执行不同的操作。例如,输入 1 后,程序会提示输入联系人姓名,然后显示该联系人的详细信息:

Enter your choice: 1 Enter
Enter a name: Jorge Ruiz Enter
Name: Jorge Ruiz
Phone: 919-555-1212
Email: jorge@myschool.edu

最后,用户可以选择输入 5 退出程序。

2. 统一建模语言(UML)

在设计类时,UML(Unified Modeling Language,统一建模语言)是一个非常有用的工具。它提供了一套标准的图表,用于图形化地描述面向对象系统。

一个典型的类的 UML 图是一个被分为三个部分的盒子:
- 顶部部分 :用于书写类的名称。
- 中间部分 :列出类的数据属性。
- 底部部分 :列出类的方法。

以下是几个 UML 图的示例:
- Coin 类的 UML 图

Coin
__sideup
__init__( )
toss( )
get_sideup( )
  • CellPhone 类的 UML 图
CellPhone
__manufact
__model
__retail_price
__init__(manufact, model, price)
set_manufact(manufact)
set_model(model)
set_retail_price(price)
get_manufact()
get_model()
get_retail_price()
3. 从问题中识别类

在开发面向对象程序时,首要任务之一是识别需要创建的类。通常,目标是识别问题中存在的不同类型的现实世界对象,然后在应用程序中为这些对象类型创建类。

下面是一种简单且流行的识别类的方法,包含以下步骤:
1. 获取问题域的书面描述 :问题域是与问题相关的现实世界对象、参与者和主要事件的集合。如果你充分理解要解决的问题的本质,可以自己编写问题域的描述;否则,应该请专家来编写。
例如,假设我们要编写一个程序,供 Joe’s Automotive Shop 的经理为客户打印服务报价。专家可能会这样描述问题域:

Joe’s Automotive Shop services foreign cars and specializes in servicing cars made by Mercedes, 
Porsche, and BMW. When a customer brings a car to the shop, the manager gets the customer’s 
name, address, and telephone number. The manager then determines the make, model, and year 
of the car and gives the customer a service quote. The service quote shows the estimated parts 
charges, estimated labor charges, sales tax, and total estimated charges.

问题域描述应包括以下内容:
- 物理对象,如车辆、机器或产品。
- 人所扮演的角色,如经理、员工、客户、教师、学生等。
- 业务事件的结果,如客户订单,在本例中是服务报价。
- 记录保存项,如客户历史记录和工资记录。
2. 识别描述中的所有名词(包括代词和名词短语) :这些名词都是潜在的类。在上述问题域描述中,所有的名词和名词短语如下:

address
BMW
car
cars
customer
estimated labor charges
estimated parts charges
foreign cars
Joe’s Automotive Shop
make
manager
Mercedes
model
name
Porsche
sales tax
service quote
shop
telephone number
total estimated charges
year
  1. 精炼列表,只保留与问题相关的类 :问题描述中出现的名词只是成为类的候选者,并非所有名词都需要创建类。下面是一些常见的排除潜在类的原因:
    • 某些名词实际上表示相同的事物
      • “car”、“cars” 和 “foreign cars” 都指的是汽车的一般概念,我们可以选择保留 “car”,排除 “cars” 和 “foreign cars”。
      • “Joe’s Automotive Shop” 和 “shop” 都指的是 “Joe’s Automotive Shop” 这家公司,我们可以保留 “shop”,排除 “Joe’s Automotive Shop”。
    • 某些名词可能代表解决问题时不需要关注的项目
      • 由于我们的应用程序只需要关注单个服务报价,不需要处理或确定任何公司范围的信息,所以可以排除 “shop”。
      • 问题描述没有要求处理关于经理的任何信息,所以可以排除 “manager”。
    • 某些名词可能代表对象,而不是类
      • “Mercedes”、“Porsche” 和 “BMW” 在这个例子中都代表特定的汽车,可以被视为 “car” 类的实例,因此可以排除它们作为类。
    • 某些名词可能代表可以分配给变量的简单值,不需要类
      • 像 “address”、“estimated labor charges”、“estimated parts charges” 等,它们都是简单的字符串或数值,可以存储在变量中,因此可以排除它们作为类。

经过以上筛选,最终确定的潜在类为 “car”、“customer” 和 “service quote”。这意味着在我们的应用程序中,需要编写 Car 类、 Customer 类和 ServiceQuote 类。

4. 识别类的职责

一旦确定了类,接下来的任务是识别每个类的职责。类的职责包括:
- 类需要知道的事情,即类的数据属性。
- 类需要执行的操作,即类的方法。

我们可以通过问 “在这个问题的上下文中,类必须知道什么?类必须做什么?” 这两个问题来确定类的职责。下面我们来具体分析前面确定的三个类的职责。

4.1 Customer 类
  • 需要知道的事情(数据属性)
    • 客户的姓名
    • 客户的地址
    • 客户的电话号码
  • 需要执行的操作(方法)
    • 初始化 Customer 类的对象。
    • 设置和返回客户的姓名。
    • 设置和返回客户的地址。
    • 设置和返回客户的电话号码。

Customer 类的 UML 图如下:

Customer
__name 
__address
__phone
__init__(name, address, phone)
set_name(name)
set_address(address)
set_phone(phone)
get_name()
get_address()
get_phone()

以下是 Customer 类的 Python 代码实现:

# Customer class
class Customer:
    def __init__(self, name, address, phone):
        self.__name = name
        self.__address = address
        self.__phone = phone

    def set_name(self, name):
        self.__name = name

    def set_address(self, address):
        self.__address = address

    def set_phone(self, phone):
        self.__phone = phone

    def get_name(self):
        return self.__name

    def get_address(self):
        return self.__address

    def get_phone(self):
        return self.__phone
4.2 Car 类
  • 需要知道的事情(数据属性)
    • 汽车的品牌
    • 汽车的型号
    • 汽车的年份
  • 需要执行的操作(方法)
    • 初始化 Car 类的对象。
    • 设置和获取汽车的品牌。
    • 设置和获取汽车的型号。
    • 设置和获取汽车的年份。

Car 类的 UML 图如下:

Car
__make
__model
__year
__init__(make, model, year)
set_make(make) 
set_model(model)
set_year(year)
get_make( )
get_model( )
get_year( )

以下是 Car 类的 Python 代码实现:

# Car class
class Car:
    def __init__(self, make, model, year):
        self.__make = make
        self.__model = model
        self.__year = year

    def set_make(self, make):
        self.__make = make

    def set_model(self, model):
        self.__model = model

    def set_year(self, year):
        self.__year = year

    def get_make(self):
        return self.__make

    def get_model(self):
        return self.__model

    def get_year(self):
        return self.__year
4.3 ServiceQuote 类
  • 需要知道的事情(数据属性)
    • 估计的零件费用
    • 估计的人工费用
    • 销售税
    • 总估计费用
  • 需要执行的操作(方法)
    • 初始化 ServiceQuote 类的对象。
    • 设置和获取估计的零件费用和估计的人工费用。
    • 计算和返回销售税。
    • 计算和返回总估计费用。

ServiceQuote 类的 UML 图如下:

ServiceQuote
__parts_charges
__labor_charges
__init__(pcharge, lcharge)
set_parts_charges(pcharge)
set_labor_charges(lcharge)
get_parts_charges( )
get_labor_charges( )
get_sales_tax( )
get_total_charges( )

以下是 ServiceQuote 类的 Python 代码实现:

# Constant for the sales tax rate
TAX_RATE = 0.05

# ServiceQuote class
class ServiceQuote:
    def __init__(self, pcharge, lcharge):
        self.__parts_charges = pcharge
        self.__labor_charges = lcharge

    def set_parts_charges(self, pcharge):
        self.__parts_charges = pcharge

    def set_labor_charges(self, lcharge):
        self.__labor_charges = lcharge

    def get_parts_charges(self):
        return self.__parts_charges

    def get_labor_charges(self):
        return self.__labor_charges

    def get_sales_tax(self):
        return self.__parts_charges * TAX_RATE

    def get_total_charges(self):
        return self.__parts_charges + self.__labor_charges + (self.__parts_charges * TAX_RATE)
5. 总结

设计面向对象的应用程序是一个迭代的过程,可能需要多次尝试才能确定所有需要的类并明确它们的职责。随着设计过程的推进,我们会对问题有更深入的理解,从而找到改进设计的方法。

下面是一个简单的流程图,展示了从问题描述到确定类和类的职责的过程:

graph LR
    A[获取问题域描述] --> B[识别所有名词]
    B --> C[精炼名词列表]
    C --> D[确定潜在类]
    D --> E[识别类的职责]
    E --> F[设计类和方法]

通过以上步骤,我们可以更加系统地设计和实现面向对象的程序,提高代码的可维护性和可扩展性。

面向对象编程中的类设计与实现

6. 检查点问题解答

为了更好地理解前面所讲的内容,下面对一些检查点问题进行解答:
1. 典型的类的 UML 图的三个部分分别显示什么内容?
- 顶部部分显示类的名称。
- 中间部分显示类的数据属性。
- 底部部分显示类的方法。
2. 什么是问题域?
问题域是与问题相关的现实世界对象、参与者和主要事件的集合。
3. 在设计面向对象应用程序时,谁应该编写问题域的描述?
如果你充分理解要解决的问题的本质,可以自己编写问题域的描述;否则,应该请专家来编写。
4. 如何在问题域描述中识别潜在的类?
- 获取问题域的书面描述。
- 识别描述中的所有名词(包括代词和名词短语),这些都是潜在的类。
- 精炼列表,只保留与问题相关的类。
5. 类的职责是什么?
类的职责包括类需要知道的事情(数据属性)和类需要执行的操作(方法)。
6. 应该问哪两个问题来确定类的职责?
在这个问题的上下文中,类必须知道什么?类必须做什么?
7. 类的所有操作是否总是会在问题域描述中直接提及?
不是,有些类的职责可能不会在问题域描述中直接提及,需要进一步考虑。

7. 复习问题解答
7.1 选择题
题目 选项 答案
哪种编程实践侧重于创建与所处理的数据分离的函数? a. 模块化
b. 过程式
c. 函数式
d. 面向对象
b
哪种编程实践侧重于创建对象? a. 以对象为中心
b. 目标式
c. 过程式
d. 面向对象
d
类中引用数据的组件是什么? a. 方法
b. 实例
c. 数据属性
d. 模块
c
对象是什么? a. 蓝图
b. 饼干模具
c. 变量
d. 实例
d
通过什么方式可以将类的属性对类外部的代码隐藏起来? a. 避免使用 self 参数来创建属性
b. 属性名以两个下划线开头
c. 属性名以 private__ 开头
d. 属性名以 @ 符号开头
b
哪种方法获取数据属性的值但不改变它? a. 检索器
b. 构造函数
c. 修改器
d. 访问器
d
哪种方法将值存储在数据属性中或以其他方式更改其值? a. 修改器
b. 构造函数
c. 访问器
d. 以上都不是
c
对象创建时会自动调用哪个方法? a. init
b. init
c. str
d. object
a
如果类有一个名为 str 的方法,以下哪种方式可以调用该方法? a. 像调用其他方法一样:object. str ()
b. 将类的实例传递给内置的 str 函数
c. 对象创建时自动调用该方法
d. 将类的实例传递给内置的 state 函数
b
为图形化描述面向对象系统提供一套标准图表的是什么? a. 统一建模语言
b. 流程图
c. 伪代码
d. 对象层次系统
a
在一种识别问题中类的方法中,程序员在问题域描述中识别什么? a. 动词
b. 形容词
c. 副词
d. 名词
d
在一种识别类的数据属性和方法的方法中,程序员识别类的什么? a. 职责
b. 名称
c. 同义词
d. 名词
a
7.2 判断题
题目 答案 解释
过程式编程实践侧重于对象的创建。 错误 过程式编程侧重于创建与所处理的数据分离的函数,面向对象编程侧重于创建对象。
对象可重用性是面向对象编程使用增加的一个因素。 正确 对象可重用性可以提高开发效率,减少代码重复,是面向对象编程的一个重要优势。
在面向对象编程中,通常将类的所有数据属性都对类外部的代码可访问是常见做法。 错误 为了保证数据的安全性和封装性,通常会将一些数据属性隐藏起来,只提供特定的方法来访问和修改。
类方法不一定需要有 self 参数。 错误 在 Python 中,类的实例方法必须有 self 参数,它用于引用类的实例。
属性名以两个下划线开头会将该属性对类外部的代码隐藏起来。 正确 这是 Python 中实现数据隐藏的一种方式。
不能直接调用 str 方法。 错误 可以通过将类的实例传递给内置的 str 函数来调用 str 方法,也可以像调用其他方法一样直接调用。
找到面向对象程序所需类的一种方法是识别问题域描述中的所有动词。 错误 通常是识别问题域描述中的所有名词,然后进行筛选。
7.3 简答题
  1. 什么是数据隐藏?
    数据隐藏是指将类的某些数据属性对类外部的代码隐藏起来,只提供特定的方法来访问和修改这些属性,以保证数据的安全性和封装性。
  2. 如何使对象的数据属性对类定义外部的代码不可访问?
    在 Python 中,可以通过将属性名以两个下划线开头来实现,例如 self.__attribute
  3. 类和类的实例有什么区别?
    类是对象的蓝图或模板,定义了对象的属性和方法;而类的实例是根据类创建的具体对象,每个实例都有自己的属性值。
  4. 以下语句调用了对象的方法,方法的名称是什么?引用对象的变量名称是什么?
wallet.get_dollar()

方法的名称是 get_dollar ,引用对象的变量名称是 wallet
5. init 方法执行时,self 参数引用什么?
self 参数引用类的实例对象,通过 self 可以访问和修改实例的属性和方法。
6. 在 Python 类中,如何将属性对类外部的代码隐藏起来?
将属性名以两个下划线开头,例如 self.__attribute
7. Python 中的 str 方法返回对象状态的字符串表示。描述如何使用 str 方法来打印对象的状态。
可以将类的实例传递给内置的 str 函数,或者在需要字符串表示的地方使用该实例,Python 会自动调用 str 方法。例如:

class MyClass:
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return f"MyClass object with value: {self.value}"

obj = MyClass(10)
print(str(obj))  # 调用 __str__ 方法
8. 算法工作台示例

下面我们来完成一个算法工作台的任务,编写一个 Book 类。该类具有标题、作者姓名和页数的数据属性,并且有以下方法:
- 一个 __init__ 方法,用于接受每个数据属性的参数。

以下是 Book 类的 Python 代码实现:

class Book:
    def __init__(self, title, author, pages):
        self.__title = title
        self.__author = author
        self.__pages = pages

    def get_title(self):
        return self.__title

    def get_author(self):
        return self.__author

    def get_pages(self):
        return self.__pages

在这个 Book 类中,我们使用了 __init__ 方法来初始化对象的属性,并提供了访问这些属性的方法。

9. 总结与展望

通过前面的学习,我们了解了面向对象编程中类的设计和实现的基本方法,包括使用 UML 图进行类的设计、从问题中识别类和类的职责、编写类的代码等。同时,我们也通过复习问题和算法工作台的练习,加深了对这些知识的理解和掌握。

然而,这只是面向对象编程的一个起点。在实际的开发中,我们可能会遇到更复杂的问题和需求,需要进一步学习和掌握更多的面向对象编程技巧,如继承、多态等。以下是一个简单的流程图,展示了面向对象编程学习的一个大致路径:

graph LR
    A[学习类和对象的基本概念] --> B[掌握类的设计和实现方法]
    B --> C[学习继承和多态]
    C --> D[应用设计模式优化代码]
    D --> E[开发复杂的面向对象应用程序]

希望大家在后续的学习中,不断实践和探索,提高自己的面向对象编程能力,编写出更加高效、可维护的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值