以道御术 ——面向对象与面向过程的思想初探

本人是一名学生党,平时经常看见别人提面向对象,面向过程等术语,感觉离我很遥远。在我的印象中,我对这两个专业术语并不是很了解,在认知层面上仅仅只是停留在用C语言开发程序就是面向过程,用Java或C++开发程序就是面向对象;也知道面向对象比面向过程更加适合开发大型项目,更便于维护,至于原因什么的从未考虑。一直以来也都是本着这样的一种想法去敲代码:编程嘛,能把程序写出来就行了,我最近学的是什么语言就用什么去写。从未考虑过合不合适,也从来没有在意过如编程思想这般深入的问题。但学习本就应该是知其然,也要知其所以然。如果只是懂得了一门编程语言而不懂编程思想,又怎么能够理解编程语言背后精妙之处呢?而且现实生活中,也经常会遇到一种语言不太适合解决,需要使用另外一种语言来解决的情况。倘若能够理解编程思想并灵活运用,在那么学习语言上,想必遗留的应该也只是一些语法上的问题了吧!?由此可见编程思想是道,而编程语言则是术,以道御术,方是正途。最近看书学习中,开始对这两种思想有了一个初步的了解。

         要了解面向对象,首先就要知道什么是对象。正所谓一切皆对象,通俗地讲,对象就是我们根据现实中事物的特性进行模拟的实体。换句话来说,做项目就是要让计算机来代替我们完成一些任务,而其中不可或缺的便是场景,事物和规则。其中事物便是我们所说的对象,事代表对象所能完成的操作,物代表实体。以C++为例举个通俗的例子,我们可以定义一个人类,其中包含了姓名和身份证号等属性,具有吃饭和睡觉等行为。

class Human{
private:
	string m_name;
	string m_id;
public:
	Human(string const& name = “”,string const& id = “0”):m_name(name),m_id(id){}
	void eat(void){
		cout << “My name is ” << m_name << “.” << endl;
		cout << “My id is ” << m_id << ”.” << endl;
		cout << “I’m eating.” << endl;
	}
	void sleep(void){
		cout << “My name is ” << m_name << “.” << endl;
		cout << “My id is ” << m_id << ”.” << endl;
		cout << “I’m sleeping.” << endl;
	}
};

在这个类中,我们根据程序需求对人这个现实生活中所存在的客观事物进行了抽象,只留下了跟需求有关的吃饭和睡觉等行为。对于除此之外的人类的其他行为我们一概不理。但在C++中,这样的类并不占内存,它只是一个抽象的概念。只有当我们对类进行实例化,为其分配内存时,才会成为一个对象。即类是对对象的抽象,对象是类的实例。

         对于一个软件项目来说,一个程序是使用面向对象还是使用面向过程,是根据现实需求来决定的。在要求实时性和运行效率高的情况下,如硬件编程和嵌入式软件开发中,使用面向过程的语言来编写会更加合适,但要考虑非常多的细节,代码量也不会太高。而在大型项目开发的过程中,使用面向对象的语言来编写会更加容易,也便于后期的维护。下面以一个常见的计算器程序为例。

C语言:

//1
int add(int x,int y){
	return  x + y;
}
//2
int sub(int x,int y){
	return  x – y;
}
//3
int mul(int x,int y){
	return  x * y;
}
//4
int div(int x,int y){
	return x / y;
}
int main(void){
	int x,y,z;
	printf(“请输入两个数,以,分割:”);
	scanf(“%d,%d”,&x,&y);
	printf(“请输入接下来的操作(1,2,3,4):”);
	scanf(“%d”,&z);
	switch(z){
	case 1:
		printf(“x + y = %d\n”,add(x,y));
		break;
	case 2:
		printf(“x - y = %d\n”,sub(x,y));
		break;
	case 3:
		printf(“x * y = %d\n”,mul(x,y));
		break;
	case 4:
		if(0 == y){
			printf(“0不能作为除数\n”);
			break;
		}
		printf(“x / y = %d\n”,div(x,y));
		break;
	default:
		printf(“无效操作\n”);
		break;
	}
	return 0;
}
//C++:
…
class Calculator{
private:
	int m_numberA;
	int m_numberB;
public:
	Calculator(int x = 0,int y = 1): m_numberA(x), m_numberB(y){}
	void add(void){
		cout << “x + y = ” << m_numberA + m_numberB << endl;
	} 
	void sub(void){
		cout << “x - y = ” << m_numberA - m_numberB << endl;
	}
	void mul(void){
		cout << “x * y = ” << m_numberA * m_numberB << endl;
	}
	void div(void){
		if(m_numberB != 0) 
			cout << “x / y = ” << m_numberA / m_numberB << endl;
		else
			cout << “除数不能为0” << endl;
	}
};
int main(void){
	Calculator cal(10,5);
	cal.add();
	cal.sub();
	cal.mul();
	cal.div()
	return 0;
}


如果单从逻辑角度上来看,使用面向对象会比使用面向过程更为直观一些,模块层次更明显。Calculator本身就是一个计算器,我们只需实例化它就能使用。

面向过程以步骤划分模块,面向对象以功能划分模块。我们将计算器单独抽象成一个类,将计算功能细分成为四则运算。我们只需要从界面上获取数据信息,再交给cal对象处理即可。而在面向过程中,我们先从界面获取数据,再根据用户的需求选择相应的函数处理。界面与业务处理之间存在耦合,数据之间的关系松散,非常容易被改变,而且一旦被改变就有可能影响其他函数的调用。在面向对象中,对象与对象之间是一种静态的关系,在没有收到外在条件的驱动下,对象与对象之间的数据不会发生交互,不会影响其他对象的运行。这样便使得项目组之间的工作分配更为简单,在保证一定沟通的情况下,可以将功能抽象成一个个的类,只留出需要被调用的功能接口,调用者只负责调用而不必理会实现细节。这也更符合人类的思考习惯。

另一方面,面向对象在代码复用上更具优势。比如说我们要扩展计算器程序的功能,增加某个新的功能,这个新的功能中包含了某些旧的功能。从面向过程的角度来看,我们可以将相应功能函数的代码复制到新功能函数中来实现复用。当然你也可在新功能函数中去调用旧功能函数,但是这样会带来额外的函数调用开销。如果新功能的实现需要反复使用到旧功能,那么这样做的代价就是使整个程序的性能下降。而且,一旦复制了旧功能代码,会使得程序的重复代码增加,不利于后期的维护。从面向对象的角度来看,我们可以从Calculator类中派生出一个新的类CalculatorEx,并在其中去增加一个成员函数来实现新的功能。在新功能函数中去调用旧功能函数。由于面向对象编程语言的语言特性,在同一个对象中调用其他的成员函数的开销会更小。

除了以上提到的两个方面,面向对象还提供了多态。从基类继承而来的多个子类在接收到同样的信息时会调用自身的函数,而不会调用到基类的函数。以C++为例,从基类继承得到的子类,在重写了基类中的虚函数后,可以通过基类指针来使用多态。

class Shape{
public:
	virtual void show(void) const{
		cout << “Shape” << endl;
	}
};
class Circle:public Shape{
public:
	void show(void) const{
		cout << ”Circle” << endl;
	}
};
class Rectangle:public Shape{
public:
	void show(void) const{
		cout << ” Rectangle” << endl;
	}
};
int main(void){
	Circle c;
	Rectangle r;
	Shape* s = &c;
	s->show();
	s = &r;
	s->show();
	return 0;
}


程序运行完后结果为:

Circle

Rectangle

总结:

1.      在解决大型软件项目,面向对象以功能来划分模块,相对于面向对象的以步骤来划分模块而言,面向对象能够更为清晰的描述需求,逻辑层次分明。

2.      面向对象所具有的抽象性,能够从客观的视角来看问题,也符合人类的思考习惯。将共性抽象成基类,再在子类中去赋予它个性。

3.       面向对象的三大要素:封装,继承和多态

面向过程的三大要素:顺序,分支和循环相比之下,面向对象可以更为灵活的解决问题,减少编码量。使用封装可以降低不同模块数据之间的耦合度,提高模块自身数据的聚合度,符合”高内聚,低耦合”的编程原则。使用继承可以更加优雅的复用代码。相对于面向过程的纯代码级复用,面向对象的继承机制可以降低程序中重复冗余的代码,同时也避免了过大的函数调用开销,提高了程序的可维护性。使用多态则可以使程序更加灵活,在接受相同的信息时可以根据对象的实际类型的不同调用不同的成员函数,已完成不同的需求。

相比之下,面向对象可以更为灵活的解决问题,减少编码量。使用封装可以降低不同模块数据之间的耦合度,提高模块自身数据的聚合度,符合”高内聚,低耦合”的编程原则。使用继承可以更加优雅的复用代码。相对于面向过程的纯代码级复用,面向对象的继承机制可以降低程序中重复冗余的代码,同时也避免了过大的函数调用开销,提高了程序的可维护性。使用多态则可以使程序更加灵活,在接受相同的信息时可以根据对象的实际类型的不同调用不同的成员函数,已完成不同的需求。

但是仍需注意的是,本文中所提到的面向对象和面向过程等都是编程思想,仅仅只是以C语言和C++语言进行一个对比。所谓的面向对象的语言只不过是语言特性和编译器支持能够更加方便地支持面向对象的开发方式罢了。即便是使用C++也可以面向过程,使用C语言亦可实现面向对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值