<!-- [if gte mso 9]><xml><w:WordDocument><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><w:DisplayHorizontalDrawingGridEvery>0</w:DisplayHorizontalDrawingGridEvery><w:DisplayVerticalDrawingGridEvery>2</w:DisplayVerticalDrawingGridEvery><w:DocumentKind>DocumentNotSpecified</w:DocumentKind><w:DrawingGridVerticalSpacing>7.8</w:DrawingGridVerticalSpacing><w:View>Normal</w:View><w:Compatibility></w:Compatibility><w:Zoom>0</w:Zoom></w:WordDocument></xml><![endif]-->
应用OOP
的设计过程演化
面向对象的程序设计(Object-Oriented Programming
,简记为
OOP)
立意于创建软件重用代码,具备更好地模拟现实世界环境的能力,这使它被公认为是自上而下编程的优胜者。它通过给程序中加入扩展语句,把函数“封装”进编程所必需的“对象”中。面向对象的编程语言使得复杂的工作条理清晰、编写容易。
在计算时代的早期,程序员基于语句思考编程问题。到了20
世纪七八十年代,程序员开始基于子程序去思考编程。进入
21
世纪,程序员以类为基础思考编程问题。而类是
OOP
中的核心组成元素,通常都是使用类来“封装”对象(属性、行为)。在经典图书《代码大全》里定义:“创建高质量的类,第一步,可能也是最重要的一步,就是创建一个好的接口。这也包括了创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏在抽象背后。”
为了更好的理解设计思想,本系列文章以简单的《书店信息系统》为例,但随着需求的增加,程序将越来越复杂。此时就有修改设计的必要,重构和设计模式就可以派上用场了。最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了。
在一个书店里,主要业务就是销售书,销售书后所得到的就是收取到的资金(本次交易金额),那以这个业务来分析,在不考虑设计的情况下,我们该怎么去实现:
1namespace EBook.Step1
2{
3
/**//// <summary>
4
///
会员购书
5
/// </summary>
6
public class Buy
7
{
8
/**//// <summary>
9
///
处理销售书的方法
10
/// </summary>
11
public void Execute()
12
{
13
Console.WriteLine("
会员购买了一本书
");
14
}
15
16
/**//// <summary>
17
///
买书得到了多少钱
18
/// </summary>
19
public void GetMoney()
20
{
21
Console.WriteLine("
收到了
xx.xx
元
RMB");
22
}
23
}
24}
这是针对书店的会员购书的业务逻辑,那如果是普通的顾客来购书呢?此时我们不得不为普通的顾客提供专门的服务(建立普通顾客业务逻辑类):
1namespace EBook.Step1
2{
3
/**//// <summary>
4
///
普通顾客购书
5
/// </summary>
6
public class SBuy
7
{
8
public void Execute()
9
{
10
Console.WriteLine("
普通顾客购买了一本书
");
11
}
12
13
public void GetMoney()
14
{
15
Console.WriteLine("
收到了
xx.xx
元
RMB");
16
}
17
}
18}
而客户端通过判断顾客的类型来决定调用具体的类来处理相应的操作:
1namespace EBook.Step1
2{
3
class Program
4
{
5
static void Main(string[] args)
6
{
7
string uType = Console.ReadLine();
8
switch (uType)
9
{
10
case "
会员
": Member(); break;
11
case "
普通顾客
": General(); break;
12
}
13
}
14
15
private static void General()
16
{
17
SBuy sbuy = new SBuy();
18
sbuy.Execute();
19
sbuy.GetMoney();
20
}
21
22
private static void Member()
23
{
24
Buy buy = new Buy();
25
buy.Execute();
26
buy.GetMoney();
27
}
28
}
29}
仔细分析这段代码,虽然我们已经应用了OO
的思想,将不同的顾客分为不同的对象来处理,但这样的设计同样很糟糕。也许你已经看出,这糟糕之处就是在
switch
这里。
不错,如果书店的客户不只是上述所提到的两种类型,还有如黄金会员,白金会员,白银会员,普通会员......
等等一系列的划分,随着业务的扩展,将来会员类型也许还会不断的增加,那么就会去修改
switch
不断的增加相应的会员处理逻辑,然后让
switch
子句越来越长,直至达到你需要无限的拉动滚动条才能看到
switch
的结束。如上设计的
UML
图如下:
在上面的设计中,我们已经应用到了OO
的思想,把不同个顾客类型做为一独立的对象来处理。仔细观察,会员(
Buy
)和普通顾客具有完全相同的方法,为什么不为它们建立一个共同的父类呢?
通过共性的抽象,让会员和普通顾客都去继承并实现父类的抽象方法,那代码是这样的吗?
1/**//// <summary>
2/// 抽象出销售书的父类
,
所以的销售行为都继承于它。
3/// </summary>
4public abstract class Sell
5{
6
/**//// <summary>
7
///
处理销售书的方法
8
/// </summary>
9
public abstract void Execute();
10
11
/**//// <summary>
12
///
卖书得到了多少钱
13
/// </summary>
14
public abstract void GetMoney();
15}
16-----------------------------------------------
17/**//// <summary>
18/// 会员购书
19/// </summary>
20public class Buy:Sell
21{
22
public override void Execute()
23
{
24
Console.WriteLine("
会员购买了一本书
");
25
}
26
27
public override void GetMoney()
28
{
29
Console.WriteLine("
收到了
xx.xx
元
RMB");
30
}
31}
32----------------------------------------------
33/**//// <summary>
34/// 普通顾客购书
35/// </summary>
36public class SBuy:Sell
37{
38
public override void Execute()
39
{
40
Console.WriteLine("
普通顾客购买了一本书
");
41
}
42
43
public override void GetMoney()
44
{
45
Console.WriteLine("
收到了
xx.xx
元
RMB");
46
}
47}
我们通过抽象,引用了继承的思想,使整个设计也有了OOP
的味道。
然而从现实生活中来分析,销售逻辑是一个抽象层,而我们所针对的是具体的顾客类型,出了在程序里应用多态特性,Sell
类并没有实际使用的情况,这就是为何将其设计为抽象类及抽象方法,而不是使用普通的类里定义虚方法(
virtual
)的方式来实现。对应在设计中,就是:这个类永远不会被实例化,实例化的是它的子类。
此时,客户端的调用可以直接依赖于抽象层(Sell
),不过这样的设计实质并没有多大的变化,客户端还是需要通过判断决定该调用那一个具体的实现。
1public class Resolve
2{
3
/**//// <summary>
4
/// //
依赖于抽象
5
/// </summary>
6
/// <param name="sell"></param>
7
public void Execute(Sell sell)
8
{
9
sell.Execute();
10
sell.GetMoney();
11
}
12}
13
14namespace EBook.Step2
15{
16
//
依赖于抽象
,
有了继承
,
有了
OO
的味道。
17
class Program
18
{
19
static void Main(string[] args)
20
{
21
//
会员
22
new Resolve().Execute(new Buy());
23
Console.WriteLine("n-----------------------------n");
24
//
普通顾客
25
new Resolve().Execute(new SBuy());
26
}
27
}
28}
29
这里我们先不谈客户端调用去判断顾客的类型。从现在的设计来看,即满足了类之间的层次关系,同时又保证了类的最小化原则,更利于扩展。即使你现在要增加如黄金会员(Gold)
和白金会员
(Platinum)
等会员类型,只需要设计
Gold
和
Platinum
类,并继承
Sell
,重写
Execute
和
GetMoney
方法即可,而
Resolve
类对象的
Execute
方法根本就不用改变。
针对如上的设计来说,完全可以满足一个简单的销售逻辑的处理,可算是一个完美的设计。然而,在我们的实际项目中会有很多意想不到的事发生,其中需求变更应该是最为头疼的。刁钻的客户是永远不会满足的,这意味着我们就不能给我们的设计画上圆满的句号。时间久了,书店的一些书籍早已因陈旧而不能销售出去,可老板又不想让这些书成为废品,书无论是新还是旧都有他的价值所在,旧书的里的知识或许是不能与新版的书籍比配,但还是有一定的参考价值,就如我们去研究历史一样,是为了什么?是为了更好的迎接未来。
书店的业务扩展,老板决定将陈旧的书籍用来出租(
呵呵,这想法不错,满足了像我这样的穷人想看书可又没钱买书的
XX
,
UPUP.....)
,根据我们上面在设计销售经验来看,那出租我们应该怎么来设计呢?是不是也应该把不同的对象做为的独立的逻辑来处理呢?答案是肯定的,那到底要怎么去设计呢,这要求我们深入到具体的业务逻辑了。
通过分析现实中的业务逻辑,出租主要涉及到两个方面:租借和归还。而我们上面的设计中把顾客分为了会员和普通顾客两类,那么归还是不是应该划分为会员还书和普通顾客换书呢?这是肯定的,因为会员和普通顾客在租书的租金上是不一样的,会员和普通顾客在租金上应该是两种不同的策略。
从上面的分析得出,出租主要分为租借、会员归还和普通顾客归还这三种类型的逻辑。而租书不用给租金但必须先交押金,还会则需要收取租金(可从押金中扣除)。也就是说这三种类型里都回有处理出租(租借和归还)和交易金额的逻辑。既然都有共性,那也应该抽象出父类,是这样设计的吗?
1namespace EBook.Step3
2{
3
/**//// <summary>
4
///
作为租赁业务的一个基类,所以的租赁行为都继承于它。
5
/// </summary>
6
public abstract class Hire
7
{
8
/**//// <summary>
9
///
处理租赁书的方法
10
/// </summary>
11
public abstract void Execute();
12
13
/**//// <summary>
14
///
租书所得到的租金
15
/// </summary>
16
public abstract void GetMoney();
17
}
18}
我们来看看UML
草图:
1/**//// <summary>
2/// 租书
3/// 分析
:
租书的时候是不需要支付租金的
,
但是需要支付押金
4/// </summary>
5public class Rent:Hire
6{
7
/**//// <summary>
8
///
执行出租逻辑
9
/// </summary>
10
public override void Execute()
11
{
12
Console.WriteLine("
租出一本
XXX
书
");
13
}
14
15
/**//// <summary>
16
///
计算出租后所得到的租金
17
/// </summary>
18
public override void GetMoney()
19
{
20
Console.WriteLine("
得到了
XX.XX
元的租金
");
21
}
22}
1/**//// <summary>
2/// 还书
3/// 会员还书--租金和普通顾客的租金有区别
4/// </summary>
5public class MBack:Hire
6{
7
/**//// <summary>
8
///
执行还书逻辑
9
/// </summary>
10
public override void Execute()
11
{
12
Console.WriteLine("
会员还书
");
color: #333333; font-size: 9pt; font-family:
分享到:
相关推荐
结构化编程强调自顶向下、分层设计,通过逐步细化来构建程序,但它的模块间关系简单,数据和代码分离,导致了数据和过程的独立性,限制了代码的复用性。在结构化编程中,程序员需要针对不同数据类型编写不同的处理...
随着计算机科学的发展,编程语言和编程范式也在不断地演变和进步。面向对象编程(Object-Oriented Programming,简称OOP)已成为现代软件开发中的核心概念之一,尤其是在C++和Java这类高级编程语言中得到了广泛应用...
在上一篇文章里(应用OOP的设计过程演化(二))完善了整个系统的体系结构,以及完成了各个具体的功能角色的功能,这也只能算是完成了一个结构而已,要真正做到完善还差得很远。比如在计算租金这个算法上,使用switch...
计算机语言的演变是计算机科学发展的核心部分,反映了人类与机器沟通方式的进步。这一历程可以分为三个主要阶段:机器语言、汇编语言和高级语言。 1. 机器语言:计算机最初的语言是基于二进制的机器语言,由0和1...
康威生命游戏,简称生命游戏,是由数学家约翰·康威提出的一种细胞自动机,它以简单的规则模拟了复杂的生命演化过程。本项目gameoflife.net是基于.NET平台,采用行为驱动开发(BDD)和测试驱动开发(TDD)以及面向...
《C++设计与演化》是Bjarne Stroustrup所著的一本经典著作,它深入探讨了C++语言的设计哲学、发展历程以及其背后的决策过程。这本书对于理解C++的核心特性,以及如何有效地利用这些特性来编写高效、可维护的代码至关...
### C++语言的设计与演化 #### 一、引言 《C++语言的设计和演化》是一本关于C++编程语言发展历程及其设计...通过学习C++的演变过程,我们不仅能更好地理解这门语言,还能从中获得对软件工程和编程实践的深刻见解。
2. **面向对象编程(OOP)**:C++是最早支持OOP概念的语言之一,包括类、封装、继承和多态性。Stroustrup详细解释了这些概念如何在C++中实现,并探讨了它们的实际应用。 3. **模板**:C++的模板机制允许创建泛型...
1. **面向对象编程**:C++是一种支持面向对象编程(OOP)的编程语言,它引入了类、对象、继承、封装和多态等概念,使得代码更加模块化和易于维护。在本书中,Stroustrup深入剖析了这些特性的设计动机和实现细节。 2...
【从面向过程到面向对象】的演变历程是编程思想的重大进化。面向过程编程,始于IBM公司的约翰·巴库斯在1957年推出的Fortran,这种编程方式以功能分解为核心,采用自顶向下的结构化方法,将复杂问题拆解为一系列小...
Stroustrup作为C++的创始人,以其独特的视角和深厚的理论基础,详细阐述了C++从诞生到发展过程中所经历的关键设计决策和演化过程。 本书首先介绍了C++语言的基础,包括面向对象编程(OOP)的概念,如类、对象、封装...
C++的核心机制包括面向对象编程(OOP)、泛型编程(GP)和概念化编程(Concepts)。面向对象特性如类、继承、封装和多态为构建复杂系统提供了结构;泛型编程通过模板实现了代码复用,允许编写适用于多种数据类型的...
《C++语言的设计和演化》是Bjarne Stroustrup所著的一本经典著作,它深入探讨了C++编程语言的起源、设计理念以及其在发展过程中经历的重大变革。这本书是理解C++内在机制和设计哲学的关键,对于任何想要深入学习C++...
标题 "OOP_Project_group12:火势蔓延模拟" 暗示这是一个使用面向对象编程(Object-Oriented Programming, OOP)技术实现的项目,旨在模拟火势蔓延的过程。项目可能由一组成员(group12)合作完成,旨在通过编程来...