55 个解决方案
#1
就是将函数指针作为另一个函数的参数。
你这么理解就差不多了。
你这么理解就差不多了。
#2
个人认为主要说的是考虑问题方面:要从对外表现来考虑系统,而非一定要实现的内部功能。尽管这两者的区别不是很明显。
#3
to redguardtoo() : 有没有例子阿?那样的话,比较好说明问题吧.
to abcsxl(SIM) : 看不懂??!
to abcsxl(SIM) : 看不懂??!
#4
举个例单的例子,
private List list = new ArrayList();
public List getList(){
return list;
}
一个接口抽象出一个类的契约,你针对接口编码了,方便不同的子类进行替换,类与类间的关系更松散。上面的bean属性声明为List,明显要比声明为一个更具体的ArrayList要更好,它没有绑定那一个具体的实现子类上。
private List list = new ArrayList();
public List getList(){
return list;
}
一个接口抽象出一个类的契约,你针对接口编码了,方便不同的子类进行替换,类与类间的关系更松散。上面的bean属性声明为List,明显要比声明为一个更具体的ArrayList要更好,它没有绑定那一个具体的实现子类上。
#5
其实我觉得"针对接口编程"和DIP原则是一样的,只是表述不同。
针对接口编程说的也是依赖倒置,接口是抽象类,实现是具体派生类,DIP原则说,你的程序也应该依赖抽象,而不是具体。所谓的依赖就是使用抽象类的指针或者引用,去掉用方法(大多是派生类的方法),而不是使用派生类的指针或者引用去调用这些方法。
其实有一个很简单的判定方法,如果你的程序中,出现了派生类的类名,则你的程序依赖实现,或者依赖具体,如果没有出现派生类的类型,只出现抽象类的类名,则你的程序只依赖抽象,或者只依赖接口。
如果你的程序只依赖抽象,那么这部分程序就是针对接口的编程。
针对接口编程说的也是依赖倒置,接口是抽象类,实现是具体派生类,DIP原则说,你的程序也应该依赖抽象,而不是具体。所谓的依赖就是使用抽象类的指针或者引用,去掉用方法(大多是派生类的方法),而不是使用派生类的指针或者引用去调用这些方法。
其实有一个很简单的判定方法,如果你的程序中,出现了派生类的类名,则你的程序依赖实现,或者依赖具体,如果没有出现派生类的类型,只出现抽象类的类名,则你的程序只依赖抽象,或者只依赖接口。
如果你的程序只依赖抽象,那么这部分程序就是针对接口的编程。
#6
比如说你要定义一个类实现一个数的二次方运算,下面这样的定义是针对接口而非实现的:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Set(const int nData){ m_nData = nData; }
int Get()const{ return m_nData; }
int Square()const{ return m_nData*m_nData; }
friend istream&operator>>(istream&in, CObj&obj)
{ /*OMIT*/}
};
你在main函数里面可以这样调用:
int main()
{
CObj obj;
cin>>obj;
cout<<obj.Get()<<"的平方是:"<<obj.Square()<<endl;
return 0;
}
如果是面向实现的那么这个类就是这样的了:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Square(){
cin >> m_nData;
cout<<m_nData<<"的平方是:"<<m_nData*m_nData<<endl;
}
};
main函数里面这样调用:
int main()
{
CObj obj;
obj.Suqare();
return 0;
}
不知道楼主明白没有?
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Set(const int nData){ m_nData = nData; }
int Get()const{ return m_nData; }
int Square()const{ return m_nData*m_nData; }
friend istream&operator>>(istream&in, CObj&obj)
{ /*OMIT*/}
};
你在main函数里面可以这样调用:
int main()
{
CObj obj;
cin>>obj;
cout<<obj.Get()<<"的平方是:"<<obj.Square()<<endl;
return 0;
}
如果是面向实现的那么这个类就是这样的了:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Square(){
cin >> m_nData;
cout<<m_nData<<"的平方是:"<<m_nData*m_nData<<endl;
}
};
main函数里面这样调用:
int main()
{
CObj obj;
obj.Suqare();
return 0;
}
不知道楼主明白没有?
#7
受益匪浅
#8
个人认为是java的用法:订一一个interface,但是不是可以实例化的实体类。
#9
应用了oo的多态性
#10
to mintwlf(Programmer):
正如stonespace(stonespace) 所说的程序是依赖抽象而不是依赖具体的话,你的程序里没有抽象的说法吧?
to stonespace(stonespace):
这个说法我都有点理解了,但是什么又是DIP呢?
谢谢
正如stonespace(stonespace) 所说的程序是依赖抽象而不是依赖具体的话,你的程序里没有抽象的说法吧?
to stonespace(stonespace):
这个说法我都有点理解了,但是什么又是DIP呢?
谢谢
#11
以前有过类似的帖子,博士有很经典的回答。你可以搜索一下,可能是在java框架板块中。我这里不敢造次。
#12
参考标准c库的qsort函数,如果要对任意类型的数据进行快速排序,就需要用到函数指针。
google “函数指针”或者“回调函数”也可以找到相关的资料。
在http://www.crazy-bit.com/myworks/PhoXo_Note/crazybit_1.htm上有一个例子。
google “函数指针”或者“回调函数”也可以找到相关的资料。
在http://www.crazy-bit.com/myworks/PhoXo_Note/crazybit_1.htm上有一个例子。
#13
类似于接口类型没有定死,或一系列类型,而不是定死了类型
#14
CObj的实现只是针对数据会发生的操作,并未针对具体的要求平方这样的操作,在我举的这个例子里面生成CObj这个类已经就是对需求进行抽象了。
#15
个人理解“针对接口编程,而非实现”就是这个接口可以实现当前功能,需求变化了之后用这个接口还可以实现其它功能!从而自然地实现了接口的复用!
#16
用C++表示的接口函数(interface)必须是虚函数(本质上是函数指针),否则无法在*运行时*切换接口。
要理解接口的用处,最关键的是理解商用程序都是代码行数很多的大程序。使用某个类(例如CA)的地方可能有几百处甚至几千处。在开发过程中,这个类完全可能在不断的变化(例如CA变成了CB)。如果CA的用户始终是通过接口IBase(就像例子中main函数的代码)使用该类的。那么只要IBase不变,类CA再怎么变化,他的用户也不受影响。
例子:
#include <iostream>
class IBase{
public:
virtual name(void)=0;
};
class CA:public IBase{
public:
virtual name(void){ std::cout<<"I am CA"<<std::endl;}
};
class CB:public IBase{
public:
virtual name(void){ std::cout<<"I am CB"<<std::endl;}
};
int main(int argc, char* argv[])
{
CA a;
CB b;
IBase* arr[]={&a,&b,};
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
arr[i]->name();
}
//you should see the result:
//I am CA
//I am CB
return 0;
}
BTW:
我认为学好C,学好结构化编程,理解基本概念,对于学习oo很有用处,否则只是把玩新名词,不能算掌握了oo。
要理解接口的用处,最关键的是理解商用程序都是代码行数很多的大程序。使用某个类(例如CA)的地方可能有几百处甚至几千处。在开发过程中,这个类完全可能在不断的变化(例如CA变成了CB)。如果CA的用户始终是通过接口IBase(就像例子中main函数的代码)使用该类的。那么只要IBase不变,类CA再怎么变化,他的用户也不受影响。
例子:
#include <iostream>
class IBase{
public:
virtual name(void)=0;
};
class CA:public IBase{
public:
virtual name(void){ std::cout<<"I am CA"<<std::endl;}
};
class CB:public IBase{
public:
virtual name(void){ std::cout<<"I am CB"<<std::endl;}
};
int main(int argc, char* argv[])
{
CA a;
CB b;
IBase* arr[]={&a,&b,};
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
arr[i]->name();
}
//you should see the result:
//I am CA
//I am CB
return 0;
}
BTW:
我认为学好C,学好结构化编程,理解基本概念,对于学习oo很有用处,否则只是把玩新名词,不能算掌握了oo。
#17
http://community.csdn.net/Expert/topic/3804/3804457.xml?temp=.3227808
全部引用
回复人: jeffyan77(jeffyan77) ( ) 信誉:223 2005-2-26 23:28:19 得分: 0
所有的软件设计原则都应当从OCP原则出发。DIP也不例外。
DIP的目的就是要避免在次要功能模块发生需求变化的时候,必须连带改写主要功能模块。
一个错误的依赖关系,是主要功能模块对次要功能模块的依赖。一旦次要功能模块发生改变,主要功能模块也要发生变化,这是愚蠢的。你想想,一个公司老总要依赖于一个秘书,秘书一换掉,老总也要换掉,你认为这合理吗?
DIP就是要把这个关系再倒转过来,让次要模块依赖于主要模块,这样次要模块发生改变的时候,主要模块不会被影响。
具体来讲,一个变量在声明的时候,应当尽量选择使用抽象类型进行声明,譬如在Java中不要使用
Vector allCustomers = new Vector();
而要使用
List allCustomers = new Vector();
这样在必要的时候可以改为
List allCustomers = new ArrayList();
这就消除了当前类对allCustomers变量类型的依赖。
所有的原则都不是绝对的,譬如Java语言中所有的对象都是从java.lang.Object派生出来的,而java.lang.Object本身就是一个具体类。如果要严格执行“不能继承具体类”的原则,那Java语言就完蛋了。
我在《Java与模式》一书中对这个原则进行了比较通俗的解释,你可以参考:
http://www.china-pub.com/computers/common/info.asp?id=8182
全部引用
回复人: jeffyan77(jeffyan77) ( ) 信誉:223 2005-2-26 23:28:19 得分: 0
所有的软件设计原则都应当从OCP原则出发。DIP也不例外。
DIP的目的就是要避免在次要功能模块发生需求变化的时候,必须连带改写主要功能模块。
一个错误的依赖关系,是主要功能模块对次要功能模块的依赖。一旦次要功能模块发生改变,主要功能模块也要发生变化,这是愚蠢的。你想想,一个公司老总要依赖于一个秘书,秘书一换掉,老总也要换掉,你认为这合理吗?
DIP就是要把这个关系再倒转过来,让次要模块依赖于主要模块,这样次要模块发生改变的时候,主要模块不会被影响。
具体来讲,一个变量在声明的时候,应当尽量选择使用抽象类型进行声明,譬如在Java中不要使用
Vector allCustomers = new Vector();
而要使用
List allCustomers = new Vector();
这样在必要的时候可以改为
List allCustomers = new ArrayList();
这就消除了当前类对allCustomers变量类型的依赖。
所有的原则都不是绝对的,譬如Java语言中所有的对象都是从java.lang.Object派生出来的,而java.lang.Object本身就是一个具体类。如果要严格执行“不能继承具体类”的原则,那Java语言就完蛋了。
我在《Java与模式》一书中对这个原则进行了比较通俗的解释,你可以参考:
http://www.china-pub.com/computers/common/info.asp?id=8182
#18
模式参考敏捷,GOF
那都是死的
具体要灵活运用
那都是死的
具体要灵活运用
#19
比如说你编写一个能在屏目输出HELLO,WORLD的类,但你的客户(使用该函数的人)可能要在WINDOW和UNIX两种环境下使用该函数,而在这两 种环境下输出的实现是不一样的,于是你要有一个接口,两 个实现,但对于你的客户来说,他们使用的只是那个接口,不用考虑它是怎么实惠的,从而复用了接口
CHELLO
/ \
CWINHELLO CUNIXHELLO
于是,用户使用时就:
CHELLO *p = NULL;
if(system == win)
p = new CWINHELLO;
else if(system == unix)
p = new CUNIXHELLO;
else
error;
CHELLO
/ \
CWINHELLO CUNIXHELLO
于是,用户使用时就:
CHELLO *p = NULL;
if(system == win)
p = new CWINHELLO;
else if(system == unix)
p = new CUNIXHELLO;
else
error;
#20
不用考虑它是怎么实现的
#21
可否理解为对象接口与其功能实现是分离的,不同的对象可以对请求做不同的实现.两个有相同接口的对象可以有完全不同的实现!
本人写了以下代码段(C#),希望大家对它有些看法!
using System;
namespace Mypattern
{
/// <summary>
/// StartClass 的摘要说明。
/// </summary>
class StartClass
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Worker.ExcuteDisplay(new Car());
Worker.ExcuteDisplay(new Truck());
}
}
interface Action
{
void Display();
}
class Car:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Car!");
}
#endregion
}
class Truck:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Truck!");
}
#endregion
}
class Worker
{
public static void ExcuteDisplay(Action vehicle)
{
vehicle.Display();
}
}
}
本人写了以下代码段(C#),希望大家对它有些看法!
using System;
namespace Mypattern
{
/// <summary>
/// StartClass 的摘要说明。
/// </summary>
class StartClass
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Worker.ExcuteDisplay(new Car());
Worker.ExcuteDisplay(new Truck());
}
}
interface Action
{
void Display();
}
class Car:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Car!");
}
#endregion
}
class Truck:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Truck!");
}
#endregion
}
class Worker
{
public static void ExcuteDisplay(Action vehicle)
{
vehicle.Display();
}
}
}
#22
分清责任,使用不变,封装分离易变
#23
楼主真是天生丽质啊!这么经典的名言这么快就领悟了,佩服!
#24
针对接口编程就是你只管用它的接口好了,至于里面是怎么样的不用管。
#25
好呀
#26
其实这个问题是OO的本质
要彻底理解不是靠文字游戏
建议搂主看下面向对象分析与设计
要彻底理解不是靠文字游戏
建议搂主看下面向对象分析与设计
#27
在某些场合(代码需要注重扩展性)使用抽象基类“只针对接口编程”确实是个很好的方法。
但是这一条准则的实施前提是你所写的模块很可能是需要改动、扩充的。
任何的准则都是有条件的。世界上没有绝对的准则。
但是这一条准则的实施前提是你所写的模块很可能是需要改动、扩充的。
任何的准则都是有条件的。世界上没有绝对的准则。
#28
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
#29
C++
class Interface{
virtual Func1() = NULL;
virtual Func2() = NULL;
}
接口就是纯虚抽象类.
class Interface{
virtual Func1() = NULL;
virtual Func2() = NULL;
}
接口就是纯虚抽象类.
#30
回复人: Albert_Andrew(阿尔伯特.安德鲁) ( ) 信誉:100 2005-03-20 19:01:00 得分: 0
你说的话,有意思,使我想起了诡辩.与技术无关呵呵.如下:
世界上没有绝对的准则
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
你说的话,有意思,使我想起了诡辩.与技术无关呵呵.如下:
世界上没有绝对的准则
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
#31
我的理解是动手写一个软件前就要先想好软件的各个模块是如何组装在一起的,而且这个构想一旦形成,在软件编写过程中就不允许改变,然后假设这些模块已经写好了,用这些模块把软件主体的代码写出来.接下里再对每一个模块套用这个流程,直到所有的程序全部完成.
在这样一个开发方法中,软件被分成了若干层,中间的层次对调用它的上层而言是实现,对它所包含的下层而言则是接口.
比如一个典型的三层应用软件,从上到下分成了表现层,业务层,数据层,那么你所设计的业务层的那些对象,它们的属性和方法对表现层而言就是实现(表现层调用这些业务层对象来实现软件界面的数据输入/显示),对数据层而言就是接口(数据层针对业务层对象定义的属性和方法到数据库中取出相应的数据).
明白了吗?
在这样一个开发方法中,软件被分成了若干层,中间的层次对调用它的上层而言是实现,对它所包含的下层而言则是接口.
比如一个典型的三层应用软件,从上到下分成了表现层,业务层,数据层,那么你所设计的业务层的那些对象,它们的属性和方法对表现层而言就是实现(表现层调用这些业务层对象来实现软件界面的数据输入/显示),对数据层而言就是接口(数据层针对业务层对象定义的属性和方法到数据库中取出相应的数据).
明白了吗?
#32
分析中要注意,通常有N个高内聚的对象体系都要通过接口通信。比如WIN内的消息-事件体系中用的就是接口。完整理解C的指针和(结构与联合)的人,不难理解OO原则。有些人还用C实现了OO编程,所以,C语言不要浅尝。
#33
感觉mintwlf(Programmer)的例子很有道理
#34
好强啊~学习中^^^^^^
#35
针对接口编程是面向对象的一个原则,在编程之前要确定功能,而功能就是在接口中定义的
#36
"针对接口编程,而非实现"也有个度的问题,没有必要所有的类都先写个接口然后实现它再在别的地方针对接口编程,太繁琐,我一般在下面几个情况下才会用这种方法
1,跨层调用的类
2,有可能变化的类
3,通用性较强的类(比如说数据访问或是一些业务无关的算法)
1,跨层调用的类
2,有可能变化的类
3,通用性较强的类(比如说数据访问或是一些业务无关的算法)
#37
mark
#38
针对接口编程也就是使你程序通用行更好了。一般情况下,如果实现接口的方法代码改变,不要对客户端代码进行修改,这样应该说是比较好的。这个也要在实践中不停的锻炼自己,从中找出一些经验和体会,我个人就是深有体会的。另外现在也有很多成熟的设计模式可以供参考。
#39
想想在最早的c上的函数传递参数的时候得地址传递和值传递,就是型参和值参
#40
IMyInterface aInstance = TheInstanceFactory.getInstance();
我的理解就是这里所说的一切都是针对软件开发当中模块间的关联复杂度的问题。
大家都知道要“低耦合,高内聚”的原则,这样一个模块的变动对另一个模块影响
最小。这样设计的系统的扩展伸缩性能得到很大的提高。
对于一些项目出现经常性的变动,良好的设计可以把复杂度降到最低。
我的理解就是这里所说的一切都是针对软件开发当中模块间的关联复杂度的问题。
大家都知道要“低耦合,高内聚”的原则,这样一个模块的变动对另一个模块影响
最小。这样设计的系统的扩展伸缩性能得到很大的提高。
对于一些项目出现经常性的变动,良好的设计可以把复杂度降到最低。
#41
如果是C++的话,“接口”和“纯虚基类”可以认为是几乎等同的,这样楼主实现起来不就有标准了?
#42
关于这一点,在《COM技术内幕》中有比较详细的论述。
#43
学习中
#44
先举一个实例吧 —— 主板
就是计算机里的主板。
主板上有许多插槽 —— AGP PCI ...... (其他的记不住了:))
插槽就是接口
插槽上可以插好多东东 cpu 显卡 声卡 内存 硬盘 键盘 鼠标
只要是符合接口协议的东东都可以识别。
比如 PCI(主板上有好几个一样的插槽,一般是白色的) 它上面可以查显卡、声卡、网卡、猫、电视卡等等。
主板(板载声卡显卡网卡的除外)
并没有实现这些功能(显示图像、发出声音、上网、接收电视信号)
需要你自己网上插卡,查什么卡你的电脑就有了什么功能。
呵呵 应该差不多了吧。
这是我对接口的理解。
asp.net用过吗?
里面的DataGrid 用过吗?
DataGrid 就相当于主板。
使用的时候要先设置列的信息,模版列了什么的。
呵呵
就是计算机里的主板。
主板上有许多插槽 —— AGP PCI ...... (其他的记不住了:))
插槽就是接口
插槽上可以插好多东东 cpu 显卡 声卡 内存 硬盘 键盘 鼠标
只要是符合接口协议的东东都可以识别。
比如 PCI(主板上有好几个一样的插槽,一般是白色的) 它上面可以查显卡、声卡、网卡、猫、电视卡等等。
主板(板载声卡显卡网卡的除外)
并没有实现这些功能(显示图像、发出声音、上网、接收电视信号)
需要你自己网上插卡,查什么卡你的电脑就有了什么功能。
呵呵 应该差不多了吧。
这是我对接口的理解。
asp.net用过吗?
里面的DataGrid 用过吗?
DataGrid 就相当于主板。
使用的时候要先设置列的信息,模版列了什么的。
呵呵
#45
首先要明确什么是实现继承,什么是接口继承。
实现继承的目的是为了代码重用,即父类定义了一个方法,实现了一个具体功能,子类通过继承同时也具备了同样的功能,这里不涉及到多态。接口继承是指复写父类的方法,但是提供该方法的不同的子类实现,目的是主要为了使用多态。
针对实现编程,主要是为了减少冗余代码,更方便的实现功能扩充,对程序的高内聚低耦合没什么帮助。针对接口编程,父类和子类分别提供不同的接口实现(或者父类是纯虚),这样在应用中一个基类指针可以用来表示不同的类对象,通过多态提供足够的灵活性。
针对接口编程而不要针对实现编程,或者使用接口继承而不是实现继承,是颇为极端的说法,或者是作者为了突出强调接口的说法。实际应用中,都是两者结合的,目标是建立灵活高效的程序。
很多时候这些都是矛盾的,需要程序员自己去体会,比如多用组合少用继承,谁都知道组合更灵活,但是随着类的增多,关系更复杂了,而这种关系比继承体系的关系更难把握。
实现继承的目的是为了代码重用,即父类定义了一个方法,实现了一个具体功能,子类通过继承同时也具备了同样的功能,这里不涉及到多态。接口继承是指复写父类的方法,但是提供该方法的不同的子类实现,目的是主要为了使用多态。
针对实现编程,主要是为了减少冗余代码,更方便的实现功能扩充,对程序的高内聚低耦合没什么帮助。针对接口编程,父类和子类分别提供不同的接口实现(或者父类是纯虚),这样在应用中一个基类指针可以用来表示不同的类对象,通过多态提供足够的灵活性。
针对接口编程而不要针对实现编程,或者使用接口继承而不是实现继承,是颇为极端的说法,或者是作者为了突出强调接口的说法。实际应用中,都是两者结合的,目标是建立灵活高效的程序。
很多时候这些都是矛盾的,需要程序员自己去体会,比如多用组合少用继承,谁都知道组合更灵活,但是随着类的增多,关系更复杂了,而这种关系比继承体系的关系更难把握。
#46
受益非浅啊. 我是在看petshop3.0的时候理解的.以前知道好处.但是不懂用
#47
petshop3.0说的很详细,很经典。
#48
抽象出,相同类型对象的最基本,操作共性和共有属性
构件抽象基类,也就是接口
构件抽象基类,也就是接口
#49
同意 stonespace(stonespace) 的看法。这样可以保证类的稳定性,并遵循开发封闭原则。
#50
是不是说要达到高内聚低耦合
达到程序结构的稳定性和程序的灵活和可扩展性
达到程序结构的稳定性和程序的灵活和可扩展性
#1
就是将函数指针作为另一个函数的参数。
你这么理解就差不多了。
你这么理解就差不多了。
#2
个人认为主要说的是考虑问题方面:要从对外表现来考虑系统,而非一定要实现的内部功能。尽管这两者的区别不是很明显。
#3
to redguardtoo() : 有没有例子阿?那样的话,比较好说明问题吧.
to abcsxl(SIM) : 看不懂??!
to abcsxl(SIM) : 看不懂??!
#4
举个例单的例子,
private List list = new ArrayList();
public List getList(){
return list;
}
一个接口抽象出一个类的契约,你针对接口编码了,方便不同的子类进行替换,类与类间的关系更松散。上面的bean属性声明为List,明显要比声明为一个更具体的ArrayList要更好,它没有绑定那一个具体的实现子类上。
private List list = new ArrayList();
public List getList(){
return list;
}
一个接口抽象出一个类的契约,你针对接口编码了,方便不同的子类进行替换,类与类间的关系更松散。上面的bean属性声明为List,明显要比声明为一个更具体的ArrayList要更好,它没有绑定那一个具体的实现子类上。
#5
其实我觉得"针对接口编程"和DIP原则是一样的,只是表述不同。
针对接口编程说的也是依赖倒置,接口是抽象类,实现是具体派生类,DIP原则说,你的程序也应该依赖抽象,而不是具体。所谓的依赖就是使用抽象类的指针或者引用,去掉用方法(大多是派生类的方法),而不是使用派生类的指针或者引用去调用这些方法。
其实有一个很简单的判定方法,如果你的程序中,出现了派生类的类名,则你的程序依赖实现,或者依赖具体,如果没有出现派生类的类型,只出现抽象类的类名,则你的程序只依赖抽象,或者只依赖接口。
如果你的程序只依赖抽象,那么这部分程序就是针对接口的编程。
针对接口编程说的也是依赖倒置,接口是抽象类,实现是具体派生类,DIP原则说,你的程序也应该依赖抽象,而不是具体。所谓的依赖就是使用抽象类的指针或者引用,去掉用方法(大多是派生类的方法),而不是使用派生类的指针或者引用去调用这些方法。
其实有一个很简单的判定方法,如果你的程序中,出现了派生类的类名,则你的程序依赖实现,或者依赖具体,如果没有出现派生类的类型,只出现抽象类的类名,则你的程序只依赖抽象,或者只依赖接口。
如果你的程序只依赖抽象,那么这部分程序就是针对接口的编程。
#6
比如说你要定义一个类实现一个数的二次方运算,下面这样的定义是针对接口而非实现的:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Set(const int nData){ m_nData = nData; }
int Get()const{ return m_nData; }
int Square()const{ return m_nData*m_nData; }
friend istream&operator>>(istream&in, CObj&obj)
{ /*OMIT*/}
};
你在main函数里面可以这样调用:
int main()
{
CObj obj;
cin>>obj;
cout<<obj.Get()<<"的平方是:"<<obj.Square()<<endl;
return 0;
}
如果是面向实现的那么这个类就是这样的了:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Square(){
cin >> m_nData;
cout<<m_nData<<"的平方是:"<<m_nData*m_nData<<endl;
}
};
main函数里面这样调用:
int main()
{
CObj obj;
obj.Suqare();
return 0;
}
不知道楼主明白没有?
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Set(const int nData){ m_nData = nData; }
int Get()const{ return m_nData; }
int Square()const{ return m_nData*m_nData; }
friend istream&operator>>(istream&in, CObj&obj)
{ /*OMIT*/}
};
你在main函数里面可以这样调用:
int main()
{
CObj obj;
cin>>obj;
cout<<obj.Get()<<"的平方是:"<<obj.Square()<<endl;
return 0;
}
如果是面向实现的那么这个类就是这样的了:
class CObj
{
int m_nData;
public:
CObj(const int nData=0):m_nData(ndata){};
~CObj();
void Square(){
cin >> m_nData;
cout<<m_nData<<"的平方是:"<<m_nData*m_nData<<endl;
}
};
main函数里面这样调用:
int main()
{
CObj obj;
obj.Suqare();
return 0;
}
不知道楼主明白没有?
#7
受益匪浅
#8
个人认为是java的用法:订一一个interface,但是不是可以实例化的实体类。
#9
应用了oo的多态性
#10
to mintwlf(Programmer):
正如stonespace(stonespace) 所说的程序是依赖抽象而不是依赖具体的话,你的程序里没有抽象的说法吧?
to stonespace(stonespace):
这个说法我都有点理解了,但是什么又是DIP呢?
谢谢
正如stonespace(stonespace) 所说的程序是依赖抽象而不是依赖具体的话,你的程序里没有抽象的说法吧?
to stonespace(stonespace):
这个说法我都有点理解了,但是什么又是DIP呢?
谢谢
#11
以前有过类似的帖子,博士有很经典的回答。你可以搜索一下,可能是在java框架板块中。我这里不敢造次。
#12
参考标准c库的qsort函数,如果要对任意类型的数据进行快速排序,就需要用到函数指针。
google “函数指针”或者“回调函数”也可以找到相关的资料。
在http://www.crazy-bit.com/myworks/PhoXo_Note/crazybit_1.htm上有一个例子。
google “函数指针”或者“回调函数”也可以找到相关的资料。
在http://www.crazy-bit.com/myworks/PhoXo_Note/crazybit_1.htm上有一个例子。
#13
类似于接口类型没有定死,或一系列类型,而不是定死了类型
#14
CObj的实现只是针对数据会发生的操作,并未针对具体的要求平方这样的操作,在我举的这个例子里面生成CObj这个类已经就是对需求进行抽象了。
#15
个人理解“针对接口编程,而非实现”就是这个接口可以实现当前功能,需求变化了之后用这个接口还可以实现其它功能!从而自然地实现了接口的复用!
#16
用C++表示的接口函数(interface)必须是虚函数(本质上是函数指针),否则无法在*运行时*切换接口。
要理解接口的用处,最关键的是理解商用程序都是代码行数很多的大程序。使用某个类(例如CA)的地方可能有几百处甚至几千处。在开发过程中,这个类完全可能在不断的变化(例如CA变成了CB)。如果CA的用户始终是通过接口IBase(就像例子中main函数的代码)使用该类的。那么只要IBase不变,类CA再怎么变化,他的用户也不受影响。
例子:
#include <iostream>
class IBase{
public:
virtual name(void)=0;
};
class CA:public IBase{
public:
virtual name(void){ std::cout<<"I am CA"<<std::endl;}
};
class CB:public IBase{
public:
virtual name(void){ std::cout<<"I am CB"<<std::endl;}
};
int main(int argc, char* argv[])
{
CA a;
CB b;
IBase* arr[]={&a,&b,};
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
arr[i]->name();
}
//you should see the result:
//I am CA
//I am CB
return 0;
}
BTW:
我认为学好C,学好结构化编程,理解基本概念,对于学习oo很有用处,否则只是把玩新名词,不能算掌握了oo。
要理解接口的用处,最关键的是理解商用程序都是代码行数很多的大程序。使用某个类(例如CA)的地方可能有几百处甚至几千处。在开发过程中,这个类完全可能在不断的变化(例如CA变成了CB)。如果CA的用户始终是通过接口IBase(就像例子中main函数的代码)使用该类的。那么只要IBase不变,类CA再怎么变化,他的用户也不受影响。
例子:
#include <iostream>
class IBase{
public:
virtual name(void)=0;
};
class CA:public IBase{
public:
virtual name(void){ std::cout<<"I am CA"<<std::endl;}
};
class CB:public IBase{
public:
virtual name(void){ std::cout<<"I am CB"<<std::endl;}
};
int main(int argc, char* argv[])
{
CA a;
CB b;
IBase* arr[]={&a,&b,};
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
arr[i]->name();
}
//you should see the result:
//I am CA
//I am CB
return 0;
}
BTW:
我认为学好C,学好结构化编程,理解基本概念,对于学习oo很有用处,否则只是把玩新名词,不能算掌握了oo。
#17
http://community.csdn.net/Expert/topic/3804/3804457.xml?temp=.3227808
全部引用
回复人: jeffyan77(jeffyan77) ( ) 信誉:223 2005-2-26 23:28:19 得分: 0
所有的软件设计原则都应当从OCP原则出发。DIP也不例外。
DIP的目的就是要避免在次要功能模块发生需求变化的时候,必须连带改写主要功能模块。
一个错误的依赖关系,是主要功能模块对次要功能模块的依赖。一旦次要功能模块发生改变,主要功能模块也要发生变化,这是愚蠢的。你想想,一个公司老总要依赖于一个秘书,秘书一换掉,老总也要换掉,你认为这合理吗?
DIP就是要把这个关系再倒转过来,让次要模块依赖于主要模块,这样次要模块发生改变的时候,主要模块不会被影响。
具体来讲,一个变量在声明的时候,应当尽量选择使用抽象类型进行声明,譬如在Java中不要使用
Vector allCustomers = new Vector();
而要使用
List allCustomers = new Vector();
这样在必要的时候可以改为
List allCustomers = new ArrayList();
这就消除了当前类对allCustomers变量类型的依赖。
所有的原则都不是绝对的,譬如Java语言中所有的对象都是从java.lang.Object派生出来的,而java.lang.Object本身就是一个具体类。如果要严格执行“不能继承具体类”的原则,那Java语言就完蛋了。
我在《Java与模式》一书中对这个原则进行了比较通俗的解释,你可以参考:
http://www.china-pub.com/computers/common/info.asp?id=8182
全部引用
回复人: jeffyan77(jeffyan77) ( ) 信誉:223 2005-2-26 23:28:19 得分: 0
所有的软件设计原则都应当从OCP原则出发。DIP也不例外。
DIP的目的就是要避免在次要功能模块发生需求变化的时候,必须连带改写主要功能模块。
一个错误的依赖关系,是主要功能模块对次要功能模块的依赖。一旦次要功能模块发生改变,主要功能模块也要发生变化,这是愚蠢的。你想想,一个公司老总要依赖于一个秘书,秘书一换掉,老总也要换掉,你认为这合理吗?
DIP就是要把这个关系再倒转过来,让次要模块依赖于主要模块,这样次要模块发生改变的时候,主要模块不会被影响。
具体来讲,一个变量在声明的时候,应当尽量选择使用抽象类型进行声明,譬如在Java中不要使用
Vector allCustomers = new Vector();
而要使用
List allCustomers = new Vector();
这样在必要的时候可以改为
List allCustomers = new ArrayList();
这就消除了当前类对allCustomers变量类型的依赖。
所有的原则都不是绝对的,譬如Java语言中所有的对象都是从java.lang.Object派生出来的,而java.lang.Object本身就是一个具体类。如果要严格执行“不能继承具体类”的原则,那Java语言就完蛋了。
我在《Java与模式》一书中对这个原则进行了比较通俗的解释,你可以参考:
http://www.china-pub.com/computers/common/info.asp?id=8182
#18
模式参考敏捷,GOF
那都是死的
具体要灵活运用
那都是死的
具体要灵活运用
#19
比如说你编写一个能在屏目输出HELLO,WORLD的类,但你的客户(使用该函数的人)可能要在WINDOW和UNIX两种环境下使用该函数,而在这两 种环境下输出的实现是不一样的,于是你要有一个接口,两 个实现,但对于你的客户来说,他们使用的只是那个接口,不用考虑它是怎么实惠的,从而复用了接口
CHELLO
/ \
CWINHELLO CUNIXHELLO
于是,用户使用时就:
CHELLO *p = NULL;
if(system == win)
p = new CWINHELLO;
else if(system == unix)
p = new CUNIXHELLO;
else
error;
CHELLO
/ \
CWINHELLO CUNIXHELLO
于是,用户使用时就:
CHELLO *p = NULL;
if(system == win)
p = new CWINHELLO;
else if(system == unix)
p = new CUNIXHELLO;
else
error;
#20
不用考虑它是怎么实现的
#21
可否理解为对象接口与其功能实现是分离的,不同的对象可以对请求做不同的实现.两个有相同接口的对象可以有完全不同的实现!
本人写了以下代码段(C#),希望大家对它有些看法!
using System;
namespace Mypattern
{
/// <summary>
/// StartClass 的摘要说明。
/// </summary>
class StartClass
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Worker.ExcuteDisplay(new Car());
Worker.ExcuteDisplay(new Truck());
}
}
interface Action
{
void Display();
}
class Car:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Car!");
}
#endregion
}
class Truck:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Truck!");
}
#endregion
}
class Worker
{
public static void ExcuteDisplay(Action vehicle)
{
vehicle.Display();
}
}
}
本人写了以下代码段(C#),希望大家对它有些看法!
using System;
namespace Mypattern
{
/// <summary>
/// StartClass 的摘要说明。
/// </summary>
class StartClass
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
Worker.ExcuteDisplay(new Car());
Worker.ExcuteDisplay(new Truck());
}
}
interface Action
{
void Display();
}
class Car:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Car!");
}
#endregion
}
class Truck:Action
{
#region Action 成员
void Action.Display()
{
Console.WriteLine("this is Truck!");
}
#endregion
}
class Worker
{
public static void ExcuteDisplay(Action vehicle)
{
vehicle.Display();
}
}
}
#22
分清责任,使用不变,封装分离易变
#23
楼主真是天生丽质啊!这么经典的名言这么快就领悟了,佩服!
#24
针对接口编程就是你只管用它的接口好了,至于里面是怎么样的不用管。
#25
好呀
#26
其实这个问题是OO的本质
要彻底理解不是靠文字游戏
建议搂主看下面向对象分析与设计
要彻底理解不是靠文字游戏
建议搂主看下面向对象分析与设计
#27
在某些场合(代码需要注重扩展性)使用抽象基类“只针对接口编程”确实是个很好的方法。
但是这一条准则的实施前提是你所写的模块很可能是需要改动、扩充的。
任何的准则都是有条件的。世界上没有绝对的准则。
但是这一条准则的实施前提是你所写的模块很可能是需要改动、扩充的。
任何的准则都是有条件的。世界上没有绝对的准则。
#28
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
#29
C++
class Interface{
virtual Func1() = NULL;
virtual Func2() = NULL;
}
接口就是纯虚抽象类.
class Interface{
virtual Func1() = NULL;
virtual Func2() = NULL;
}
接口就是纯虚抽象类.
#30
回复人: Albert_Andrew(阿尔伯特.安德鲁) ( ) 信誉:100 2005-03-20 19:01:00 得分: 0
你说的话,有意思,使我想起了诡辩.与技术无关呵呵.如下:
世界上没有绝对的准则
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
你说的话,有意思,使我想起了诡辩.与技术无关呵呵.如下:
世界上没有绝对的准则
包括“世界上没有绝对的准则”这一句话也不是绝对的。:)
#31
我的理解是动手写一个软件前就要先想好软件的各个模块是如何组装在一起的,而且这个构想一旦形成,在软件编写过程中就不允许改变,然后假设这些模块已经写好了,用这些模块把软件主体的代码写出来.接下里再对每一个模块套用这个流程,直到所有的程序全部完成.
在这样一个开发方法中,软件被分成了若干层,中间的层次对调用它的上层而言是实现,对它所包含的下层而言则是接口.
比如一个典型的三层应用软件,从上到下分成了表现层,业务层,数据层,那么你所设计的业务层的那些对象,它们的属性和方法对表现层而言就是实现(表现层调用这些业务层对象来实现软件界面的数据输入/显示),对数据层而言就是接口(数据层针对业务层对象定义的属性和方法到数据库中取出相应的数据).
明白了吗?
在这样一个开发方法中,软件被分成了若干层,中间的层次对调用它的上层而言是实现,对它所包含的下层而言则是接口.
比如一个典型的三层应用软件,从上到下分成了表现层,业务层,数据层,那么你所设计的业务层的那些对象,它们的属性和方法对表现层而言就是实现(表现层调用这些业务层对象来实现软件界面的数据输入/显示),对数据层而言就是接口(数据层针对业务层对象定义的属性和方法到数据库中取出相应的数据).
明白了吗?
#32
分析中要注意,通常有N个高内聚的对象体系都要通过接口通信。比如WIN内的消息-事件体系中用的就是接口。完整理解C的指针和(结构与联合)的人,不难理解OO原则。有些人还用C实现了OO编程,所以,C语言不要浅尝。
#33
感觉mintwlf(Programmer)的例子很有道理
#34
好强啊~学习中^^^^^^
#35
针对接口编程是面向对象的一个原则,在编程之前要确定功能,而功能就是在接口中定义的
#36
"针对接口编程,而非实现"也有个度的问题,没有必要所有的类都先写个接口然后实现它再在别的地方针对接口编程,太繁琐,我一般在下面几个情况下才会用这种方法
1,跨层调用的类
2,有可能变化的类
3,通用性较强的类(比如说数据访问或是一些业务无关的算法)
1,跨层调用的类
2,有可能变化的类
3,通用性较强的类(比如说数据访问或是一些业务无关的算法)
#37
mark
#38
针对接口编程也就是使你程序通用行更好了。一般情况下,如果实现接口的方法代码改变,不要对客户端代码进行修改,这样应该说是比较好的。这个也要在实践中不停的锻炼自己,从中找出一些经验和体会,我个人就是深有体会的。另外现在也有很多成熟的设计模式可以供参考。
#39
想想在最早的c上的函数传递参数的时候得地址传递和值传递,就是型参和值参
#40
IMyInterface aInstance = TheInstanceFactory.getInstance();
我的理解就是这里所说的一切都是针对软件开发当中模块间的关联复杂度的问题。
大家都知道要“低耦合,高内聚”的原则,这样一个模块的变动对另一个模块影响
最小。这样设计的系统的扩展伸缩性能得到很大的提高。
对于一些项目出现经常性的变动,良好的设计可以把复杂度降到最低。
我的理解就是这里所说的一切都是针对软件开发当中模块间的关联复杂度的问题。
大家都知道要“低耦合,高内聚”的原则,这样一个模块的变动对另一个模块影响
最小。这样设计的系统的扩展伸缩性能得到很大的提高。
对于一些项目出现经常性的变动,良好的设计可以把复杂度降到最低。
#41
如果是C++的话,“接口”和“纯虚基类”可以认为是几乎等同的,这样楼主实现起来不就有标准了?
#42
关于这一点,在《COM技术内幕》中有比较详细的论述。
#43
学习中
#44
先举一个实例吧 —— 主板
就是计算机里的主板。
主板上有许多插槽 —— AGP PCI ...... (其他的记不住了:))
插槽就是接口
插槽上可以插好多东东 cpu 显卡 声卡 内存 硬盘 键盘 鼠标
只要是符合接口协议的东东都可以识别。
比如 PCI(主板上有好几个一样的插槽,一般是白色的) 它上面可以查显卡、声卡、网卡、猫、电视卡等等。
主板(板载声卡显卡网卡的除外)
并没有实现这些功能(显示图像、发出声音、上网、接收电视信号)
需要你自己网上插卡,查什么卡你的电脑就有了什么功能。
呵呵 应该差不多了吧。
这是我对接口的理解。
asp.net用过吗?
里面的DataGrid 用过吗?
DataGrid 就相当于主板。
使用的时候要先设置列的信息,模版列了什么的。
呵呵
就是计算机里的主板。
主板上有许多插槽 —— AGP PCI ...... (其他的记不住了:))
插槽就是接口
插槽上可以插好多东东 cpu 显卡 声卡 内存 硬盘 键盘 鼠标
只要是符合接口协议的东东都可以识别。
比如 PCI(主板上有好几个一样的插槽,一般是白色的) 它上面可以查显卡、声卡、网卡、猫、电视卡等等。
主板(板载声卡显卡网卡的除外)
并没有实现这些功能(显示图像、发出声音、上网、接收电视信号)
需要你自己网上插卡,查什么卡你的电脑就有了什么功能。
呵呵 应该差不多了吧。
这是我对接口的理解。
asp.net用过吗?
里面的DataGrid 用过吗?
DataGrid 就相当于主板。
使用的时候要先设置列的信息,模版列了什么的。
呵呵
#45
首先要明确什么是实现继承,什么是接口继承。
实现继承的目的是为了代码重用,即父类定义了一个方法,实现了一个具体功能,子类通过继承同时也具备了同样的功能,这里不涉及到多态。接口继承是指复写父类的方法,但是提供该方法的不同的子类实现,目的是主要为了使用多态。
针对实现编程,主要是为了减少冗余代码,更方便的实现功能扩充,对程序的高内聚低耦合没什么帮助。针对接口编程,父类和子类分别提供不同的接口实现(或者父类是纯虚),这样在应用中一个基类指针可以用来表示不同的类对象,通过多态提供足够的灵活性。
针对接口编程而不要针对实现编程,或者使用接口继承而不是实现继承,是颇为极端的说法,或者是作者为了突出强调接口的说法。实际应用中,都是两者结合的,目标是建立灵活高效的程序。
很多时候这些都是矛盾的,需要程序员自己去体会,比如多用组合少用继承,谁都知道组合更灵活,但是随着类的增多,关系更复杂了,而这种关系比继承体系的关系更难把握。
实现继承的目的是为了代码重用,即父类定义了一个方法,实现了一个具体功能,子类通过继承同时也具备了同样的功能,这里不涉及到多态。接口继承是指复写父类的方法,但是提供该方法的不同的子类实现,目的是主要为了使用多态。
针对实现编程,主要是为了减少冗余代码,更方便的实现功能扩充,对程序的高内聚低耦合没什么帮助。针对接口编程,父类和子类分别提供不同的接口实现(或者父类是纯虚),这样在应用中一个基类指针可以用来表示不同的类对象,通过多态提供足够的灵活性。
针对接口编程而不要针对实现编程,或者使用接口继承而不是实现继承,是颇为极端的说法,或者是作者为了突出强调接口的说法。实际应用中,都是两者结合的,目标是建立灵活高效的程序。
很多时候这些都是矛盾的,需要程序员自己去体会,比如多用组合少用继承,谁都知道组合更灵活,但是随着类的增多,关系更复杂了,而这种关系比继承体系的关系更难把握。
#46
受益非浅啊. 我是在看petshop3.0的时候理解的.以前知道好处.但是不懂用
#47
petshop3.0说的很详细,很经典。
#48
抽象出,相同类型对象的最基本,操作共性和共有属性
构件抽象基类,也就是接口
构件抽象基类,也就是接口
#49
同意 stonespace(stonespace) 的看法。这样可以保证类的稳定性,并遵循开发封闭原则。
#50
是不是说要达到高内聚低耦合
达到程序结构的稳定性和程序的灵活和可扩展性
达到程序结构的稳定性和程序的灵活和可扩展性