一、这个对象指的是什么 (个人理解,)
这个对象指的不是C#中的实例,这个对象是个抽象的,在我的理解中,面向对象这个对象是我们需要实现的东西,简单说就比如我现在需要编写一段游戏代码,就拿目前最火的LOL来讲,在游戏中有100+的英雄,每个英雄都有属于自己的属性比如说
这个时候我们就将这个英雄作为一个对象,100+的英雄就调用这一个封装好的英雄对象来实现这100+的英雄,否则需要重新写100+的重复代码。
二、为什么要面向对象?
面向对象是想要解决系统的可维护性、可扩展性、可重用性,我们在考虑下为什么要用面向对象来解决呢?
作者:匿名用户
链接:https://www.zhihu.com/question/27468564/answer/101951302
来源:知乎
当软件还非常简单的时候,我们只需要面向过程编程:
定义函数
函数一
函数二
函数三
函数四
定义数据
数据一
数据二
数据三
数据四
最后
各种函数,数据的操作。
当软件发展起来后,我们的软件变得越来越大,代码量越来越多,复杂度远超Hello World的时候,我们的编写就有麻烦了:
函数和数据会定义得非常多,面临两个问题。
首先是命名冲突,英文单词也就那么几个,可能写着写着取名时就没合适的短词用了,为了避免冲突,只能把函数名取得越来越长。
然后是代码重复,比如你做一个计算器程序,你的函数就要确保处理的是合理的数据,这样最起码加减乘除四个函数里,你就都要写对参数进行检测的代码,写四遍或者复制粘贴四遍不会很烦,但多了你就痛苦了,而且因为这些检测代码是跟你的加减乘除函数的本意是无关的,却一定要写在那里,使代码变得不好阅读,意图模糊。
就算一个网络小说的作者,他每次写新章节时也不大可能直接打开包含着前面几百章文字的文档接着写。更正常的做法是新建一个文档,写新的一章,为的是减小复杂性,减少干扰。更何况代码那么复杂那么容易出错的东西。
随着软件业的发展,解决办法就要出来了。
代码重复,我们可以用函数里面调用函数的方法,比如把检测代码抽出来成一个独立函数,然后加减乘除四个函数运行时只要调用一下检测函数对参数进行检查就可以了。分布在四个函数里的重复代码变成了一个函数,是不是好维护多了。
命名冲突,我们就把这一堆函数们进行分类吧。
比如没有分类时候,我们取名只能取名:
检测
整数加
整数减
整数乘
整数除
复数加
复数减
复数乘
复数除
小数加
...
进行归类后
整数 {
检测
加
减
乘
除
}
复数 {
检测
加
减
乘
除
}
小数 {
检测
加
减
乘
除
}
分数 {
检测
加
减
乘
除
}
是不是一种叫做类的概念就呼之欲出了,这样我们打开一个整数类代码文件,里面就是简简单单的加减乘除四个函数,简单清晰而不会跟外面的其他加减乘除函数命名冲突。
当然,进行归类后,又有各种的问题和解决办法了,比如四个类中的检测也是应该提取出来的,所以简单的起因最终发展出什么继承衍生之类挺复杂的一套编程模式。然后学术界那帮人就又乱起什么高大上的名字了,所谓面向对象程序设计去祸害大学里那帮孩子。
就算未来出来一个什么新的面向XX编程,我们也不用多想为什么会出现,肯定是为了解决麻烦而已。
上面进行归类后,代码其实还是不好维护的,然后我们就继续提取为:
数 {
检测
加
减
乘
除
}
整数 {
沿用上面数的设计
}
小数 {
沿用上面数的设计
}
所谓继承,就是数这个类的整体设计,沿用给整数,分数小数这些类,作为他们的编写大纲去编写加减乘除这些函数的具体代码。根据整数,分数,小数各自的性质,做出各自的调整。
这时数这个类,如果你给它里面的加减乘除函数的写了一些很粗糙简单的代码,就叫做父类,基础类。子类们“继承”了父类(把代码进行了复杂化)。
如果没写,那这个类其实就只是个设计图,叫做抽象类。子类们“实现”了抽象类(把空空的设计变成了具体代码)。
模版是什么?像C++这种复杂成狗的语言是强类型的,就是给变量进行了类型区分的,比如整数类型,双整数类型。很明显这两种变量所能容纳的数据体积是不一样的,单个函数不能通吃多种类型的参数,我们就可能会面临下面两套代码并存的局面。
单整数类 {
单整数加
单整数减
单整数乘
单整数除
}
双整数类 {
双整数加
双整数减
双整数乘
双整数除
}
所以C艹跟其他强类型语言为我们提供了一个所谓模版功能:
<变量类型>整数 {
<变量类型>加
<变量类型>减
<变量类型>乘
<变量类型>除
}
整数类等于把变量类型设置为整数,套上模版 双整数类等于把变量类型设置为双整数,套上模版
这样就写了一份代码,得到了两份类的代码。
当然,弱类型的编程语言,比如JavaScript或者PHP是没有这种烦恼的,因为变量没有类型之分。但变量类型有时候还是很重要的,弱类型语言里就会出现类似数加字符串这种运算,可能并不是程序员的预期和本意,所以比起强类型性语言而言经常会出现很多无聊的BUG。
再更新:
上面发展出了父类之后,我们发现编程还是有问题的,小数类:
小数类 {
加
减
乘
除
}
如果我们需要一个能自动实现结果四舍五入的小数计算类,同时又需要一个不需要的,怎么办呢,难道要写两个类吗?不要。
所以做出了“实例”或者“对象”这一东西,首先把类改成:
小数类 {
标识变量:是否四舍五入
标识变量:是否限定小数点后位数
构造函数(设置上面的标识)
加(会根据上面两个标识变量输出不同结果)
减(会根据上面两个标识变量输出不同结果)
乘(会根据上面两个标识变量输出不同结果)
除(会根据上面两个标识变量输出不同结果)
}
这样,我们就写一个类,但是通过构造函数,把一份代码,构造出了行为稍微有点不同的两个实例供我们使用,这时候名词来了,不能进行实例化微调化的类,叫做静态类,函数们的行为是固定的。不能实例化的类,其实只是函数们的一个集合归纳,只是对函数进行了整理,功能的强大和编码的自由灵活度是不够的。
能够进行实例化,变化出各种行为各自不大一样的实例的类,我们一般就把它们叫做类了,因为最常见。
看完之后是不是觉得恍然大悟呢?面向对象减少了我们的代码书写量,将可以重复使用的代码进行封装,
讲到这里再说下面向对象的三大特征,他们分别是封装,继承和多态
封装:找到变化并且把它封装起来,你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分,这是所有设计模式的基础,就是封装变化,因此封装的作用,就解决了程序的可扩展性。
继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,因此也解决了系统的重用性和扩展性,但是继承破坏了封装,因为他是对子类开放的,修改父类会导致所有子类的改变,因此继承一定程度上又破坏了系统的可扩展性,所以继承需要慎用,只有明确的IS-A关系才能使用,同时继承在在程序开发过程中重构得到的,而不是程序设计之初就使用继承,很多面向对象开发者滥用继承,结果造成后期的代码解决不了需求的变化了。因此优先使用组合,而不是继承,是面向对象开发中一个重要的经验。
多态:接口的多种不同的实现方式即为多态。接口是对行为的抽象,刚才在封装提到,找到变化部分并封装起来,但是封装起来后,怎么适应接下来的变化?这正是接口的作用,接口的主要目的是为不相关的类提供通用的处理服务,我们可以想象一下。比如鸟会飞,但是超人也会飞,通过飞这个接口,我们可以让鸟和超人,都实现这个接口,这就实现了系统的可维护性,可扩展性。
因此面向对象能实现人们追求的系统可维护性,可扩展性,可重用性。面向对象是一种编程思想,起初,“面向对象”是专指在程序设计中采用封装、继承、多态等设计方法,但面向对象的思想已经涉及到软件开发的各个方面,比如现在细分为了面向对象的分析(OOA),面向对象的设计(OOD),面向对象的编程实现(OOP)
三、面向对象的实现
面向对象是一种思想,他让我们在分析和解决问题时,把思维和重点转向现实中的载体中来,它分为面向对象的分析(OOA),面向对象的设计(OOD),面向对象的编程实现(OOP)三个大的步骤。
1、首先是分析需求,先不要思考怎么用程序实现它,先分析需求中稳定不变的内容都是些什么,这些内容之间的关系是什么。
2、把第一步分析出来的需求,通过进一步扩充模型,变成可实现的、符合成本的、模块化的、低耦合高内聚的模型。
3、开始动手实现这个模型~