勇哥的面向对象的练习题----动物的运动会

勇哥注:

为了方便大家做面向对象的实践,特地收集了几个例子:“动物的运动会”、“计算器”、

“养猪场的故事”。

它们都有参考答案,见下载。

这篇里勇哥只写出需求和知识点而不直接提供代码,以方便大家做编码练习。


常见的设计原则:

面向对向程序的三大特征:
封装,隐藏内部实现
继承,复用现有代码
多态,改写对象行为

设计模式的核心思想:
(1)设计模式最根本的意图是适应需求的变化。
(2)编码应采用高内聚低耦合设计思想
(3)针对接口编程,而不要针对实现编程
(4)优先使用聚合,而不是继承
(5)坚持开闭原则(OCP)

内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事,它描述的是模块内的功能联系;
耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、
进入或访问一个模块的点以及通过接口的数据。

开闭原则(OCP)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,
其它很多的设计原则都是实现开闭原则的一种手段。
开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;
开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。


此实例是一本讲解设计模式的书籍中提到的一个例子。讲得非常好。

勇哥对其做了一点点改编,放这里共享给大家做为面向对象的一个实例。


目录:

(一)动物运动会

1.1。 封装

1.2。 继承多态

1.3。重构

1.4。动物工厂

1.5。抽象类与接口

1.6。集合

1.7。泛型

1.8。组件

1.9。反射工厂

1.10。委托与事件


动物运动会

话说我们是本界动物运动会组织者。
最早有猫和狗前来报名。


1.1。 封装

这些狗和猫的比赛,项目是“叫”,谁叫得次数多谁赢。

用类封装两个对象:猫和狗。

他们都有:

字段:Name     名字
属性:ShoutNum   表示叫声的次数,最大值10
方法:Shout()       叫()

image.png

(图1)


1.2。 继承多态

现在组织者发现,未来可能有更多的飞禽走兽会来报名,于是把它们统一泛化为“动物”。

创建一个动物(Animal)类,猫和狗类都继承动物类。

在下面功能中:

1。“动物报名” 是实例化几个动物

2。 ”叫声比赛“  是遍历所有报名的动物,让他们叫(Shout方法)

image.png

(图2)


提示:

1. Animal动物类是父类,猫和狗也是动物,它们做为子类。因此相同功能的代码应该尽量放到父类
2. 既然有了父类,那么要注意访问修饰符,不要总是只有Private和Public,还应该考虑protected、internal
3. 子类如果要调用基类(父类)的方法,怎么做?
4. “叫声比赛”这里实际上要用到多态,因为不同的对象(动物)要执行相同的动作(叫)
5.  一群动物,这里要求使用数组实现。(主要是为突出后面的知识点)

知识点:

1。 狗不能继承猫,否则狗都能上树和抓老鼠了

2。 那什么时候用继承合理呢?
   两个类之间是"is-a"关系的时候就可以考虑用继承了,因为这表示一个类是另一个类的特殊种类
   例如"狗is-a动物"(狗是一只动物)
   如果两个类之间是"has-a"的关系时,表示某个角色具有某一项责任,此时不适合用继承。
   比如人有两只手,手不能继承人;再比如飞机场有飞机,所以飞机不能继承机场。

3。继承是有缺点的,父类变,则子类不得不变。
   继承会破坏包装,父类实现细节暴露给子类,这其实是增大了两个类之间的耦合性。

4。什么叫“耦合性”,简单的理解就是耦断丝连,两个类尽管分开,但一方变化都会影响到另一方,
   这就是耦合性高的表现。所以继承显然是一种类与类之间的强耦合关系。
   
5。多态是面向对象的三大特征之一,那是啥意思呢?
   一对唱京剧的父子,一天父亲病了,于是儿子决定模仿父亲上台表演(不能让观众发现)。
   这里有几点要注意:
   第一,子类以父类的身份出现(儿子上台模拟父亲表演)
   第二,子类在工作时以自己的方式来实现(儿子模仿得很好,也只是模仿,
     儿子只能用自己理解的表现方式去模仿父亲的作品)
   第三,子类以父类的身份出现时,子类特有的属性和方法不可以使用。(儿子表演有自己的绝活,
     但是代父亲表演时,自己的绝活不能表现出来)
   如果父亲还有别的儿子会表演,也可以此时代表父亲上场,道理是一样的。这就是多态。
   
6。 为了使子类的实例完全接替来自父类的类成员,父类必须将该成员声明为虚拟。
  这就要使用Vitual关键字。
  子类可以选择使用override关键字,将父类实现替换为它自己的实现,这就叫做方法覆写。


1.3。重构


重构的由来:

1。现在牛和羊也来报名,要参加‘叫声比赛’。
   虽然它们两个类可以继承动物类Animal,但有一个问题:
   在Shout()函数中,各种动作除了叫声音(哞、咩、喵、汪)不同,其它内容完全相同。
   要想办法消灭重复的东西,这就是需要重构的原因
   
2。重构时,要考虑重复相同的东西都往父类放。

如下图所示:红框处就是我们要消灭掉的重复部分。

image.png

(图3)


1.4。动物工厂

看目标动作:

勇哥选择猫,点3次报名。狗一次报名,羊一次报名。

然后“叫声比赛”。

(图4)


提示:

1。 你得根据选择的动物,创建出对象数组。这里请使用简单类工厂模式来解决。
2。 我们要求对象数组最大10个元素。



1.5。抽象类与接口

动物运动会还有一项比赛是让有特异功能的动物展示才能的。
比如本例的机器猫和石头猴子孙悟空。
机器猫的比猫多的本领是口袋里可以掏出23世纪的工具,孙悟空经猴子多的本领是72般变化。

有了这两个对象后,类的继承情况如(图6)所示。


看目标动作:

“动物报名”可以随便往对象数组放几只猫和狗。

“叫声比赛”如动画所示。

“变出东西”这是本题的重点。

它是由猫产生一个特别的分支对象“叮铛猫”

由猴子产生一个特别的分支对象“石头猴子孙悟空”

(图5)


image.png

(图6)




知识点:

1。本例会用到抽象类和接口

2。之前的动物类Animal其实根本不能实例化,因为实例化一个动物,是没有意义 
  的,一个动物长什么样子?
  动物只是个抽象的名词,没有具体的对象与之对应。
  
3。因此我们完全可以考虑把实例化没有意义的父类改为抽象类。

4。把类和方法声明为abstract,即是抽象类和抽象方法。

5。抽象类的特点:
   第一。抽象类不能实例化,因为没有意义
   第二。抽象方法是必须被子类重写的方法(注意是必须)
   第三。只要一个类中有一个抽象方法,这个类就必须定义为抽象
   
6。抽象类通常代表一个抽象概念,它提供一个继承的出发点当设计一个新的
   抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构
   里面,树叶节点应是具体类,而树枝节点均应当是抽象类。程序员应努力
   做到这一点。(见图7)
   
7。比如,若猫狗牛羊是最后一级,那么它们就是具体类,但如果还有更下面
   一级的金线猫继承于猫,哈巴狗继承于狗,就需要考虑把猫和狗改成抽象
   类了,当然这不是绝对,还是需要具体情况具体分析的。
   
8。关于本例为啥要用接口的讨论
   首先,该怎么定义机器猫和孙悟空的特殊能力呢? 
   这些本领都可以看做是“变出东西”,那“变出东西”的能力给机器猫和
   孙悟空可以吗?
   显示是不可以的,要想用多态,至少得让猫、猴有“变出东西”的能力,
   而为了通用性,最好让动物有“变出东西”的行为。这样多态就肯定没问题
   了。
   但是我们不能这样做,“变出东西”不可能是动物的方法,而是机器猫和孙
   悟空的专用方法。
   
9。为了解决第8条提出的问题,可以使用接口(Interface)。

10。 接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一
    旦类实现了接口,类就可以支持接口所指定的所有属性和成员。
    声明接口的语法上与声明抽象类完全相同,但不允许提供接口中任何成员的
    执行方式(即不能有方法体,只有方法定义)。
    
11。接口不能实例化,不能肝构造方法和字段,不能有修饰符,如public等。
    不能声明虚拟或静态类。
    实现接口的类必须要实现接口中所有方法和属性。
    一个类可以实现多个接口,多个类可以实现同一个接口。
12。引入接口后,完美解决了8条所述的问题。见(图8)

13。引入的接口,做一件事“变出东西”
    对于机器猫,在使用“变出东西”方法是掏出23世纪的工具,而孙悟空是72变。
    这样,接口就完成多态性。
    
14。接口与抽象类有什么区别呢?
    抽象类可以给出一些成员的实现,接口却不能包含成员的实现。
    抽象类的抽象成员可被子类部分实现,接口的成员需要实现类去完全实现。
    一个类只能继承一个抽象类,但是可以实现多个接口。
    以上是从两者功能形态上去区分,还有三点:
    第一。类是对象的抽象;抽象类是对类的抽象;接口是对行为的抽象。接口是
        对类的局部(行为)进行的抽象,而抽象类是对类整体(字段、属性、方法)
        的抽象。如果只关注行为的抽象,那么也可以认为接口就是抽象类。
        总之,不论是接口、抽象类、类、甚至对象,都是在不同的层次、不同角度
        进行抽象的结果,它们的共性就是抽象。
    第二。如果行为跨越不同类的对象,可使用接口;对于一些相似的类对象,用
        继承抽象类。如猫狗它们都是动物,它们之间有很多相似的地方,所以应该
        让它们去继承动物这个抽象类。
        而飞机、乌鸦、超人是完全不同的类。本类的机器猫和孙悟空一个是动漫,一
        个是神话人物,这也是不相关的类,但它们的共同点是:前三个是“都会飞”,
        后二个是“变东西”,所以此时让它们去实现相同接口达到我们的设计目的就
        很适合了。
        实现接口和继承抽象类并不是冲突的,我们完全可以让超人继承人类,再实现
        飞行接口。
        超人本事很多,刀枪不入,力大无穷,飞行这些行为可以用多个接口来实现。
        这就是为什么C#中一个类只继承一个抽象类,却可以支持实现多个接口的由来。
    第三。这一点更加关键,从设计角度讲,抽象类是从子类中发现了公共的东西,泛
        化出父类,然后子类继承父类,而接口是根本不知道子类的存在,方法如何
        实现还不确认,预先定义。
        这其实说明的是抽象类和接口设计的思维过程。
        比如“动物运动会”开篇的时候,我们是有了猫和狗这两个类,观察后发现它们
        的相似之处,于是才决定泛化出Animal类,这也体现出敏捷开发的思想:通过
        重构改善即有代码的设计。
        事实上,只有猫的时候,你就去设计一个动物类,这就极可能成为过度设计了。
        所以说抽象类往往都是通过重构得来的。
        当然如果你事先意识到多种分类的可能,那么事行设计出抽象类也是完全可能的。
        而接品就完全不是一回事,比如“动物大会”的主办方在策划时,考虑需要组织
        什么样的比赛,大家商议后,觉得应该设置诸如:“跑得最快”、“跳得最高”、
        “飞得最远”、“叫得最响”、“力气最大”等等项目,而此时,主办方其实还
        不太清楚会有什么样的动物来参加运动会,所有比赛项目都可能是完全不相同的
        动物在比,它们将如何去实现这些行为不得而知,此时,能做的事就是事先定义
        这些比赛项目的行为接口。
        这样看来,“抽象类是自底而上抽象出来的”,而“接口则是自顶向下设计出来的”



image.png

(图7)


image.png

(图8)



1.6。集合

讨论为个话题是因为:之前的例子是使用数组来保存动物对象。

数组的弱点之一是创建时必须指定元素数量,不能动态的增加内容。另外一个弱点是两个

元素之间插入元素也比较困难。

这样如果对于之前的例子里多于10只动物过来报名就没办法弄了。

数组的优点是:内存中连续,下标随机访问,快速修改元素


这里请使用集合来改造代码。


知识点:

1。.net Framework的System.Collections命名空间里提供了一批用于数据存储和
   检索的专用类,这些类统称为集合。其功能有堆栈、队列、列表、哈希表等。大
   多数集合实现相同的接口,ArraryList也是其中的一种。
2。对于ArrayList,它使用了IList接口,因此它有了大小可按需要动态增加的能力。
   IList接口定义了集合用到的一些方法,ArraryList对这些方法进行了具体的实现。
   例如IList提供添加、插入、移除某一范围元素的方法
3。使用方法
   IList arrayAnimal=new ArrayList();
   当然,你也可以直接用实现类  ArraryList arraryAnimal=new ArraryList();
   还可以使用泛型接口IList<T>,如 IList<Animial> arrayAnimal=new List<Animal>();
   实现这个泛型接口的集合是List<T>,而不是ArrayList。
   泛型集合的好处下面会说到。
4。假设 ArrayAnimal.Add了5只动物。由于索引是从0开始的。因此如果连续两句移除元素1和2:
   arraryAnimal.RemoveAt(1);
   arraryAnimal.RemoveAt(2);
   这样是达不到效果的,而是要写成:
   arraryAnimal.RemoveAt(1);
   arraryAnimal.RemoveAt(1);
   这是因为执行RemoveAt(1)之后,后同的对象都自动向前移了一位,它自动保证元素的连续性。
5。 ArrayList.Add的元素是Object,也就是说放入string,char,int,double,class都可以。
    因此,ArrayList不是类型安全的。如果是值类型放进ArrayList,需要装箱为Object对象。
    使用集合元素时,还需要进行拆箱操作,这就带来很大的性能损耗。
6。 所谓装箱就是把值类型打包到Object引用类型的一个实例中。比如整形i被“装箱”并赋值给对象o
    int i=123;
    object o=(object)i; //装箱
    所谓拆箱是指从对象中提取值类型,如:
    object o=123;
    i=(int)o;
    相对于简单的赋值而言,装箱和拆箱过程需要进行大量的计算。对值类型行装箱时,必须分配
    并构造一个全新的对象。其次,拆箱所需要的强制转换也需要进行大量的计算[MSDN]。
    总之装箱拆箱是耗资源和时间的。而ArrayList集合在使用值类型数据时,其实就是在不断的做装
    箱和拆箱工作,所以效率较低。


1.7 泛型


利用泛型集体合再次改造代码。


知识点

1。 泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和
方法所存储或使用的一个或多个类型的占位符。
泛型集合类可以将类型参数用作它所存储的对象的类型的占位符;类型参数作为其字段的类
型和其方法的参数类型出现[MSDN]

比如:IList<Animal> arrayAnimal
表示这个集合变量只能接受Animal类型,其它不可以。
也可以直接声明 List<Animal> arrayAnimal
此时, arrayAnimal.Add(123)是不可以的,它只接受Animal。
这样的好处是List<T>这样的集合,相当于是集成了ArrayList和Array数组优点于一身的好东西。

2。通常情况下,都建议使用泛型集合,因为这样可以获得类型安全的直接优点而不需要从基集
合类型派生并实现类型特定的成员。
此外,如果集合元素为值类型,泛型集合类型的性能通常优于对应的非泛型集合类型(并优于从
非泛型基集合类型派生的类型),因为使用泛型时不必对元素进行装箱[MSDN]


1.8。组件


这里指的组件,就是把你的逻辑代码放到dll库文件中。

你的项目文件就应该是下面这样的:

image.png

其中AnimalLibary就是一个dll

image.png




1.9。反射工厂


我们在1.4节的“动物工厂”,使用了一组case来跟据动物的学名来创造出对应类的实例。

image.png

这里说的反射工厂我们要使用反射构建一个没有case语句的类工厂。

所谓的反射,解释如下:

C# 中的反射是一种在运行时动态获取、操作对象的能力,例如可以使用反射在运行时获取类的信息,
动态调用类的方法和属性,获取方法的参数等等。
例如,一个类Dog,可以使用反射获取这个类的信息,如名称、属性等,
然后动态的创建该类的实例,并且调用其中的方法。

注意:反射对系统是一种非常大的性能损耗,因此不能将其用在速度要求极高的代码中。



1.10。委托与事件

现在的比赛是几十只猫狗在比赛“叫”
当它们“叫”的时候,裁判根据叫的次数来判断名次。分出金银铜三组名次
裁判在每只动物身放了监听设备,当动物“叫”的时候,裁判就立刻能听到。


注意:这里,勇哥要求你用委托和事件各完成一遍本题的要求。




2023/2/25 结束语:

有兴趣做此练习的读者把自己的练习结果发给勇哥,必回复。

邮箱: 496103864@qq.com

QQ:  496103864




本文知识点的参考资料:


C# 反射的知识点实验程序

http://www.skcircle.com/?id=460


C# 反射取得类的属性名、类型、值

http://www.skcircle.com/?id=607


勇哥的C#知识经验圈点:委托

http://www.skcircle.com/?id=2150


勇哥的C#知识经验圈点:事件

http://www.skcircle.com/?id=2151


勇哥的C#知识经验圈点:泛型

http://www.skcircle.com/?id=2153




参考作业:


支付5元或购买VIP会员后,才能查看本内容!立即支付升级会员查询订单



--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2023-02-23 22:56:03 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864