reallike(认真学习Cpp用Cpp考虑delphi)和ZyxIp(绝望中...)昨天看到你们的讨论,小弟受益无穷。从昨天一直想到现在。想我原来我的做法我的想法。对于面向对象一直不得要领。还想请教: 我从半年前开始用DELPHI写程序,在此以前一直用VB做东东,确切的说VB并不是完全面向对象的语言(这样说没错吧?),或者是我本人没有这种思想吧。用现在的眼光来看,原来我应该是一直是面向过程编程。 我以前做过一个点歌系统(VB写的),看代码几乎没有面向对象的概念在里面。所以,我想用DELPHI重写一遍,用面向对象的方法去写。练练手也好,刚好我又比较熟悉这些业务。其实点歌系统没有什么东西,所以我把定义了以下几个类: T点歌钮=class(Tbutton) //多加些属性
T歌曲=class //总的歌曲
T分类歌曲=class(T歌曲) //分类的歌曲
T单曲=class(T分类歌曲) //对单一歌曲的描述
基本就是这些对象了,不知道这样分对不对?然后就是一些控制性的过程及函数。这些过程和函数需不需要也归类成一个对象?还是怎么样?还是把它们分散到T单曲或T分类歌曲的类中?
对于面向对象,我真是一知半解,请两位看一下我这样做好不好?怎么样做才算是符合面向对象?还有就是,如果我定义了这些类之后,我怎么样去在实际的开发中便用它们?比如我现在要把歌曲数据从数据库里面取出来然后显示到LISTVIEW里?怎么样去通过定义好的对象做到面向对象编程? 呵呵,不好意思,可能提的问题太多了,我当然希望两位能够帮忙回答这些问题,但是如果你们没空也没关系,我也一样感谢。如果分不够可以再开贴给分。我说用点歌系统做例子只是我比较熟悉这个业务。如果有其它例子也可以的。 总之谢谢你们。 :)
T歌曲=class //总的歌曲
T分类歌曲=class(T歌曲) //分类的歌曲
T单曲=class(T分类歌曲) //对单一歌曲的描述
基本就是这些对象了,不知道这样分对不对?然后就是一些控制性的过程及函数。这些过程和函数需不需要也归类成一个对象?还是怎么样?还是把它们分散到T单曲或T分类歌曲的类中?
对于面向对象,我真是一知半解,请两位看一下我这样做好不好?怎么样做才算是符合面向对象?还有就是,如果我定义了这些类之后,我怎么样去在实际的开发中便用它们?比如我现在要把歌曲数据从数据库里面取出来然后显示到LISTVIEW里?怎么样去通过定义好的对象做到面向对象编程? 呵呵,不好意思,可能提的问题太多了,我当然希望两位能够帮忙回答这些问题,但是如果你们没空也没关系,我也一样感谢。如果分不够可以再开贴给分。我说用点歌系统做例子只是我比较熟悉这个业务。如果有其它例子也可以的。 总之谢谢你们。 :)
解决方案 »
- 在数据导入excel后合并单元格经常出合并单元格提示如何屏蔽提示
- ntdll.dll ERROR,急
- 怎样分割这条Update语句?
- 用dbExpress页的SQLConnection1连接sql server2000怎么设置。
- ado组件什么时候连接数据库比较好?
- 一个FastReport控件简单问题?
- 如何在主窗体之前出现另一个窗体?
- 在SQL数据库里面有一个Char型的字段记录着时间值,格式为:08:05:02,我现在想查找出时间在07:00:00至10:00:00分之间的记录,应该怎样写代
- 我用timer 定时触发一个button事件,但是在程序运行中确无法关闭 窗体 谢谢了 急
- 一个相当怪的关于Delphi访问数据库的问题,很急!!!请高手进——
- 怎樣對一個動態生成的控件寫事件﹖
- c中的for循环怎么改成DELPHI的?
TDepartmentA=class(TSomeAbstractClass)
public
function RollStaffInfo(ARollTime:TDateTime;AStaffID:Word):Boolean;
end;
这样,我们在单独的单元中对这个部门以及相关动作进行了代码实现后,接下来就可以通过定义对象来模拟实际业务部门的实际业务动作!当然,一个软件的类结构中最后可的非常层次化,充分利用多态的OO特性,这样可以使软件在内部结构上复用度和可扩展性更高。
T歌曲=class //总的歌曲
T分类歌曲=class(T歌曲) //分类的歌曲
T单曲=class(T分类歌曲) //对单一歌曲的描述我看不出有一点点歌的东西。请你再写写好吗?
老实说,通过这几天看帖子,让我感觉我的delphi简直就是白学了。我就像是一直在用pascal编程序。面向对象我从来没有特意的使用过,差距太大了!
希望高手们能多结合些实例谈理论,我基础太差,理解不了呀。
我再举个例子,你们看怎么用oo设计类:一个简单的股票收支统计软件,
表operation记录了每次交易动作,包括日期,股票代码,操作,数量,价格
表stock记录了所持股票信息,包括股票代码,持股数,均价
表stockname记录了股票名称,包括股票代码,股票名称,主要作为上面两表的从表。现在有以下操作:
用户可以向operation添加记录
用户添加记录时,经过计算,自动向stock添加或删除记录
用户可以查看stock记录如果让我做,我会直接在datamodule上添加几个query,根本想不起来设计什么class,请大家指教,如果用oo的思想,应该如何设计class呢?请说得具体些,我真的好菜,谢谢了!
我上面的这些话,实际上是在重复一句话:编程等于数据结构加算法。
具体你用什么样的思想来处理上面的过程,是你的事。好,按照楼上兄弟们的说法,我们看看怎样用面向对象的思维完成这个过程。
就拿楼主的这个例子来说吧。
一个简单的股票收支统计软件,
表operation记录了每次交易动作,包括日期,股票代码,操作,数量,价格
表stock记录了所持股票信息,包括股票代码,持股数,均价
表stockname记录了股票名称,包括股票代码,股票名称,主要作为上面两表的从表。 现在有以下操作:
用户可以向operation添加记录
用户添加记录时,经过计算,自动向stock添加或删除记录
用户可以查看stock记录 如果让我做,我会直接在datamodule上添加几个query,根本想不起来设计什么class,请大家指教,如果用oo的思想,应该如何设计class呢?请说得具体些,我真的好菜,谢谢了!
看看这三个表,我要说其中Stock表少了一个关键的column:用户名,或者说用户代码。这样这三个表才能真正有了联系。
ok,看看你的动作,从你的动作中我们看到了几个名词:用户、operation表、记录、stock表。为了说明简单,我觉得在这个程序中,我们用一个类CMydatebase处理和数据库打交道,也就是说operation、stock、stockname这三个东西除了在你的CMydatebase类中能出现(一般情况下是出现在私有的成员函数和成员数据中),其他地方,这三个表是不可能出现的。好了,现在没办法,必须有一个用户类了。现在我们要有一个清醒的认识,如果你是股票买卖的用户,你在用我们这个软件时,作为系统分析员的你,你是否觉得用户需要知道有我们的软件中有一个数据库,数据库中有三个表:operation、stock、stockname。呵呵,当然不需要,用户只需要知道再敲用户代码和密码后,进入我的交易界面,能进行交易,当然只要输交易的股票代码、数量、价格、操作(买或卖),就行了。同样的,你的用户类和用户也只要知道这些事,他不需要知道有个数据库那些东东,不过作为用户类,他要和我们的CMydatBase打交道,至于你把CMydateBase作为用户类的数据成员还是作为某个成员函数的参数。或者,把用户类作为CMydateBase的数据成员或某个成员函数的参数。这些是你张开手脚的地方了。
呵呵,作为面向对象的架构师来说,在对系统分析时,首先要从用户角度看问题,哪些东西是那些东西所要知道,那些东西是那些东西不要知道的,这些是划分类的重要依据。这也是面向对象思想的重要的一个发面:你的对象的世界就是一个用户所看到的世界的一个翻版,开句玩笑说,你的类图,用户是看得懂的。
当然,面向对象的重用也是一个特点,具体怎样用,我觉得在给系统划分类时,你自然用的上。
我上面的这些话,实际上是在重复一句话:编程等于数据结构加算法。
具体你用什么样的思想来处理上面的过程,是你的事。好,按照楼上兄弟们的说法,我们看看怎样用面向对象的思维完成这个过程。
就拿楼主的这个例子来说吧。
一个简单的股票收支统计软件,
表operation记录了每次交易动作,包括日期,股票代码,操作,数量,价格
表stock记录了所持股票信息,包括股票代码,持股数,均价
表stockname记录了股票名称,包括股票代码,股票名称,主要作为上面两表的从表。 现在有以下操作:
用户可以向operation添加记录
用户添加记录时,经过计算,自动向stock添加或删除记录
用户可以查看stock记录 看看这三个表,我要说其中Stock表少了一个关键的column:用户名,或者说用户代码。这样这三个表才能真正有了联系。
ok,看看你的动作,从你的动作中我们看到了几个名词:用户、operation表、记录、stock表。为了说明简单,我觉得在这个程序中,我们用一个类CMydatebase处理和数据库打交道,也就是说operation、stock、stockname这三个东西除了在你的CMydatebase类中能出现(一般情况下是出现在私有的成员函数和成员数据中),其他地方,这三个表是不可能出现的。好了,现在没办法,必须有一个用户类了。现在我们要有一个清醒的认识,如果你是股票买卖的用户,你在用我们这个软件时,作为系统分析员的你,你是否觉得用户需要知道在我们的软件中有一个数据库,数据库中有三个表:operation、stock、stockname。呵呵,当然不需要,用户只需要知道再敲用户代码和密码后,进入我的交易界面,能进行交易,当然只要输交易的股票代码、数量、价格、操作(买或卖),就行了。同样的,你的用户类和用户也一样只要知道这些事,他不需要知道有个数据库那些东东,不过作为用户类,他要和我们的CMydatBase打交道,至于你把CMydateBase作为用户类的数据成员还是作为某个成员函数的参数。或者,把用户类作为CMydateBase的数据成员或某个成员函数的参数。这些是你张开手脚的地方了。
呵呵,作为面向对象的架构师来说,在对系统分析时,首先要从用户角度看问题,哪些东西是那些东西所要知道,那些东西是那些东西不要知道的,这些是划分类的重要依据。这也是面向对象思想的重要的一个发面:你的对象的世界就是一个用户所看到的世界的一个翻版,开句玩笑说,你的类图,用户是看得懂的。
当然,面向对象的重用也是一个特点,具体怎样用,我觉得在给系统划分类时,你自然用的上。
1。首先,我认为直接把数据库里面的数据转换到LISTVIEW,就是一种函数映射,感觉是面向过程的实现方式。这里,最贴切得面向对象实现方式就是MVC.歌曲数据是最本质的东西,也就是MVC结构里面的M(模型),而ListView很显然是V(视图)。除此之外,我认为数据库也是视图,是模型数据M在磁盘上的视图V。两个视图有着相同的模型,但两个视图却有不同的结构。数据库是关系结构,而LISTVIEW是层次结构。这样就有一个问题:M采用什么结构?我认为采用和LISTVIEW同构的层次结构是比较合理的,这样子更符合面向对象的思想。但很可惜,现在面向对象的数据库还不成熟,因此从M向数据库中的映射就必须设法实现异构间的转换。我真诚期待将来,会有savetodatabase,loadfromdatabase等等类方法,那样子就不需要现在这么多复杂的异构转换工作了。
2。MVC主要的特点就是可以保证M-V之间的一致性,我们可以采用observer模式保证这种一致性。Delphi的Framework中,很多地方都是MVC的思想,却没有提供对MVC的库支持,需要另行设计。另外,程序也许并不需要一次性读入数据库的所有字段,某些字段如歌曲wav数据等,是很庞大的字段,却不是key字段。这样,我们可以proxy模式来实现从内存到磁盘的一一映射,来提高程序的性能。
3。我认为点歌系统,用上面的方式实现,是不聪明的。因为该系统规模尚小,不需要频繁升级,也没有很大的复用价值。楼主所说的那种VB的“面向过程”的实现方式,我认为反而是最适合不过的了。当然用Delphi的“面向过程”会更好了。
4。即便是面向对象的方式,我认为:
T歌曲=class //总的歌曲
T分类歌曲=class(T歌曲) //分类的歌曲
T单曲=class(T分类歌曲) //对单一歌曲的描述
这样的分类也是不恰当的。我奉劝楼主牢记,除非是必须的模式,否则不要使用继承,而且,如果不是Framework,最多最多派生一层子类。
我认为最主要两个基类是:
T歌曲和T点歌器,前者是被动类,后者是主动类。
T歌曲应该封装歌曲应具备的play,getName,setSinger这样的过程
T点歌器应该封装choose,execute等方法。
即便仅仅两个类,具体的设计很复杂,而且没有必要。
5。在平时学习和工作中,我最感兴趣的是面向对象,用的最多的却是面向过程。面向对象,做得好的语言是java,而不是Delphi.应用程序设计,做的好的是Delphi,而不是Java.我先声明对语言没有任何偏见,更不希望跟贴中出现任何有关讨论语言好与坏的贴子。但如果单纯想学习面向对象思想,可以考虑以Java作为辅助工具。原因是:1。目前面向对象的好文章好书籍,大多数是C++/Java实现。2。Java的Framework中,更好的支持了面向对象,使用Java的Framework,实际上就是面向对象的一部分。
假设一个class有一个private方法,无论它是静态的还是动态的,如果我创建一个子类,将同名的方法声明在public中,由于我并没有对它重载,所以delphi会自动调用父类中的同名方法。也就是说,通过我创建的子类,可以访问到父类中的private方法。这样岂不是就起不到保护的目的了吗?这算不算是private的bug呢?
好,反正就面向对象这个方法论来讲。前提是找到目标对象。假设我们找到了合适的对象。申旻在他的书里面有这么一句话,设计的关键就是“抽象”。首先要明白抽象到底为我们做了什么。抽象有如下的解释:1、将复杂物体的一个或几个特性抽出去而只注意其他特性的行动或过程(如头脑只思考树本身的形状或只考虑树叶的颜色,不受它们的大小和形状的限制)。
2、将几个有区别的物体的共同性质或特性形象地抽取出来或孤立地进行考虑的行动或过程抽象对于将东西分成属及种是必需的。是的,有第一点解释就够了,够让我们知道抽象的重要性了。我们要做点歌系统,其他的都别管,我们就说点歌。嗯,点歌,我以前没有做过,好吧,现在就来做做这个东西。我在我的白板上写下了两个大字——点歌。点,是通俗的说法,也就是说作出一种选择。当东西多了的时候,选择还是比较困难的。但是我们选择之后要做什么呢?播放,对,实质其实还是播放歌曲。所以我在点歌上面写下了,播放。哦,好了,我们所有的工作其实是为了播放而作的。所以,我要建立一个抽象的基类,我在点歌左边写下了TChooseSongs哦,他的第一个过程一定是个public的,因为它就是最重要的播放。Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;为什么是虚函数后面会解释。哦,文件名应该是被保护的,所以FFileName应该在private里面所以简单的基类建立了。如果有其他的功能扩充再说。TChooseSongs = class
private
FFileName: string;
public
Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;
end;哦?如何访问FFileName呢?嗯,对了,我们需要一个方法,一个函数来传递FFileName。OK,我们就用Template Method来建立这个类,去得到FFileName。还需要一个过程来得到FFileName,不能直接来用。TChooseSongs = class
private
FFileName: string;
protected
procedure DoLoadFile(FileName: string); Virtual; abstract;
private
Procedure LoadFile(FileName: string);
Procedure GetFileName; string;
Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;
end;template mothed就是如此了,很简单的LoadFile的代码实现。Procedure TChooseSongsLoadFile(FileName: string);
begin
FFileName := FileName; //把得到的放到他该去的地方
if FileName <> '' then
begin
DoLoadFile(FileName);
end;
end;Procedure TChooseSongs.GetFileName; string;
begin
Result := FFileName;
end;下面说PlaySongs地实现。
这两个方法都会跟数据库打交道,所以还有个T数据库,里面有各个query,还有对三个表格的读取方法使用中,当一个用户登陆后,我就创建一个T用户实例,用户做操作,我就调用相应的方法。当方法涉及到数据库操作时,我又会创建一个T数据库实例,再调用其中相应的方法我理解的对吗?这种编程感觉我还真是头一次体验,看来以前我从没oo过。 ~~~>_<~~~
这么理解相当准确了。用户类承担用户类的责任,需要数据库时候,创建数据库实例,并把操作数据的一干责任交给该数据库实例。这样实现,是不是感觉类之间,责任很清晰明确?
谢谢楼上,有点感觉了。不过平日编程很少真么做的看来要改改习惯。
看看DELPHI提供給我們的控件
~~~~~~~~~~~~~~~~~~
用類寫個代碼的人就能明白OO
~~~~~~~~~~~~~~~~~~
越講越讓上不明白
~~~~~~~~~~~~~~~~~~
T歌曲=class //总的歌曲
T分类歌曲=class(T歌曲) //分类的歌曲
这种继承关系是必要的。点歌的时候,点歌器类,需要的是简单调用play功能,比如
play(Classical),play(Pop)。如果点歌程序中出现了playClassical()和playPop()两个函数,就不合理了。这时候就需要继承T歌曲,派生TClassical歌曲,和TPop歌曲,分别实现两种不同的play.注意是两种不同的实现,并没有增添附加功能。
T分类歌曲=class(T歌曲)
T单曲=class(T分类歌曲) //对单一歌曲的描述
这里就有一点不合理的地方。按照本意,T单曲是“对单一歌曲的描述”,可以说,这是一种带描述信息的歌曲,比如附有歌词等等。显然T单曲相对于T歌曲,要有额外的字段和方法,来描述歌曲信息。我们先来看看采用继承的方式,有什么问题。
T单曲=class(Tpop歌曲)//因为classical歌曲没有歌词,所以不用派生。
private
歌词;
public
get歌词;
set歌词;
end;
因为T歌曲是祖先,所以不可否认,T单曲也是T歌曲,可惜他是个不孝子,因为违背了祖上的传统,加入了新时代的东西——歌词。这个东西,是祖先所没有的。
然而,另一个类T点歌器,他是祖先T歌曲的老哥们儿,他们两个特熟悉。说的具体一些,T点歌器中,声明了T歌曲(声明的是祖先),并且调用了T歌曲的接口。那T单曲的get歌词该如何被T歌曲所认识呢?在T点歌器的眼里,T歌曲的子子孙孙都是T歌曲,为什么T单曲要多出一些功能呢?多出什么功能了?怎么调用?当然如果T歌曲家族只有一个T单曲这样的不肖子孙,还好办,大不了我T点歌器单独对待你好了(也就是重新声明一个T单曲,并且针对T单曲进行新的设计。)但如果每个子孙都像他这样,我想T点歌器一定会累吐血的。这一点,就是派生类添加新功能造成的问题。
如果要增添新功能,我们宁可采用聚合的方式,而不是派生的方式。我们把T单曲改个名字叫T带歌词的歌曲,实现如下:
T带歌词的歌曲=class
private
歌词;
聚合歌曲:T歌曲;
public
play();
get歌词();
set歌词();
end;
implementation
procedure T带歌词的歌曲。play();
begin
聚合歌曲。play();
end;
需要注意的是T带歌词的歌曲不是歌曲,但是兼容歌曲的接口.(如上所示)。通过:聚合歌曲。play()这种方式,来保证T带歌词的歌曲。play()和T歌曲.play()是相同的。这样子,T点歌器就不必再为T歌曲感到苦恼,如果他需要显示歌词的功能,简单地把T歌曲改成T带歌词的歌曲就可以了。
上面所述,概括地说,就是使用聚合而不是继承来扩展接口。
我想FrameSniper一定是误解我的意思了,你是不是说TDataModule就是后台呢?我一般把数据库作为后台考虑,而不是把TDataModule作为后台考虑。也许对于一个好的数据库设计,我在TDataModule中就什么都不用做了。但是有时候确实需要用到TDataBase这样的东东,特别是面向对象的设计过程。我曾经需要做关系数据库到层次型数据的相互转换,这个是程序所必须的且无法在数据库后台实现。(我不知道后台数据库有没有面向对象的设计方法,还望指教。)所以,我曾经设计了一个TDataBase,提供SaveToDatabase函数,实际功能是转型,把一个树形结构转化成表结构。至于那些很底层的东西,当然是靠Delphi完成了。
这算什么bug啊,你既然用了它的子类,当然可以访问private。如果你的儿子都不能拿你们家的东西,那就不算儿子了……private是防止其他类来访问。当然不能阻止你的子类继承这个。
这段话对private的描述有点问题。我不知道Delphi中是不是这样,在C++中,private表示只能被其自身类调用.他的派生类是不能调用的,但是这个private不管是成员函数还是成员数据对他的派生类来说是现实存在,可是是不可见的.如果想在派生类中调用的话,用protected关键字.
这两个方法都会跟数据库打交道,所以还有个T数据库,里面有各个query,还有对三个表格的读取方法使用中,当一个用户登陆后,我就创建一个T用户实例,用户做操作,我就调用相应的方法。当方法涉及到数据库操作时,我又会创建一个T数据库实例,再调用其中相应的方法*****************************
其本同意这样的做法,记得我在上面已经表述过,这里还有个类需要添加,那就是记录类.这个类是作为在用户类和数据库类传递数据所用的,这个类应该有些其本的数据检查等等功能.
实现用类封装,而我们在分析系统时更要用面象对象的思想。
T单个歌曲
private
ISBN
名称
类型
歌词
歌手
....................
public
property PLay
property Stop
T歌曲列表
private
FItes:Array of T单个歌曲 public
property Add
Property Del
Property Update
Property Items[Index:integer]:T单个歌曲
property Find
property GroupBy(AISBN)
Property GroupBy(NAME)
Property GroupBy(类型)
Property GroupBy(.......)
published
end;歌的分类情况根据实现操作可以单独保存出来,不用每一次都去分类。
有了这个基本的类,其它的操作都是以这为基础的。 要游泳去了,明天在聊。
结构确实很清晰,体现了面向对象封装的思想。property应该是procedure吧,还是另有原因?
返正思想是要有,但具体情况要具体对待,是在动态中求平衡的,有时为了系统实现方式的统一结构的完美,就要牺牲一些编程的方便性(比如全局变量);当然如果会对用户的操作有较大的影响,也会用一些怪异的实现方式,在不影响用户使用的前提下我倾向于实现的统一,对称,完美。
一个系统是一棵树,只有一个起点和一个终点,从上到下有层次,没有游离在外部的东西,伸手从上到下捋下去能到底。(形容的也许不恰当) 明天在来听听大家的想法。
今天刚换的水,就我们三个人,我马上就学会游泳了,爽。
各种歌曲文件的格式迥异,无法一下子说明表达。Mepg4的,DVD的,MP3的,real格式的,还有微软发布的一些格式。等等等。这个真得有点麻烦,而且还有其他的麻烦,比如说管理方面。嘿嘿,不怕,我们有派生继承这些,然后具体实现。假设是DVD点歌那很简单,就是TChooseDVDSongs = Class(TChooseSongs)
protected
DoLoadFile(FileName: string); override;
public
procedure PlaySongs(FileName: string): boolean; override;
end;然后,实现具体的有关DVD形式歌曲的的播放,如果有其他的就扩展加入。嗯,好了,到现在,实现了播放。但是,还有选择呢。嘿嘿,基类、派生类都在不断的完善,上面写的仅仅是个初稿。既然叫choose song仅仅有Play song那还不叫choose,既然叫选择,那就明确目标,问问自己:选择什么?选择歌曲。
为什么要选择?播放呗。
如何作出选择? …… 哈哈,问到点子上了,好,关键是如何作出选择。歌曲多着呢,管理困难吗?不困难,我们有强大的数据库,合理的子目录。在乎这个?我说:NO!上面所说的给歌曲加上类?T歌曲 = class?我不敢用……哈,不敢苟同,不是什么事情都需要类的,那会效率低下何必呢?建好数据库来管理,是很轻松的事情。来,分析一下目标,我们的歌曲。分析好了才能做出选择。歌啊,在上古时代就由劳动人民发明了…… (挨了一板砖)NND,谁扔我。(FS大叫:奶奶个雄不是叫你小子解释歌是啥玩艺的)。:(俺就是啰嗦没有办法。
看各位大哥的发言,认为在不偏离主题的情况下,有必要说明一下点歌系统的基本业务和我开贴内容。一般来说点歌系统有纯软件实现和硬件辅助实现的两种。我说的基本上是后一种。
点歌系统一般分成好几个部分。歌曲编辑部分主要由另一独立的程序来完成。
主要一部分则完成歌曲显示及播放。现在讨论的是后一部分。所以只会读取相应的歌曲记录。不会对歌曲数据进行相应的增删改。所以前台程序的功能主要就是实现从数据库里读入歌曲,然后提供多种检索歌曲的方法。包括对歌曲的分类等等(分类的信息由后台定义)播放则是由硬件提供的相应的API提供,其实前台功能很简单。至于我的定义:
T点歌钮=class(Tbutton) //多加些属性
因为需要实现换肤及自定义按钮功能等等所以才定义它。
T歌曲=class //总的歌曲
我想应该是从歌曲库从里读出的所有歌曲,应该包括读取歌曲,关闭歌曲库的方法,以及歌曲总数等其它属性。
T分类歌曲=class(T歌曲) //分类的歌曲
从总的歌曲里面继承下来,应该包括分类属性,分类条件属性,分类歌曲数等等属性及读取分类歌曲等方法。
T单曲=class(T分类歌曲) //对单一歌曲的描述
从分类歌曲里面继承下来,包括播放,暂停,声道切换,停唱,循环,等等操作方法,以及该单曲信息属性及该单曲当前状态属性等等。
具体怎么实现不谈,只是说按照这种业务用面向对象的方法来这样定义类好不好?到目前为止,所有的贴子里只有 wdong18(东东) 基本认同我。至于怎么样把歌曲从数据库里面读出来显示到LISTVIEW里我想应该是先调用“T歌曲”里面的读取歌曲的方法。然后再根据“T分类歌曲”分类歌曲属性,再调用显示分类歌曲的方法吧。如果用户选中歌曲,T单曲里面的相应方法来播放它。 不知道我这样想的对不对?请各位大哥讨论指正! 在此谢过
private
分类歌曲总数
分类条件
分类名
....................
function LoadSong; //根据分类条件从数据库读取分类歌曲
function ShowSong; //显示相应歌曲
.......................
这样可以了吧?
你的 T单曲=class(T分类歌曲) 我觉得很是奇怪,子类应是父类的扩展,父类应处于更抽象的一层,而你这样定义是什么意思??现实的包含关系并不对应类的层次。 分类只是一个索引一样的东西,T单曲才是最终是操作的实体。单曲的存在与分类,如何分类并没有什么关系,所以T单曲和T分类歌曲根本就不是一类,并不存在什么继承关系
还有你的 T分类歌曲=class(T歌曲) ,从根本上来说就是相同的,所有的歌曲也是分类歌曲的一种,你的意思是分类歌曲是按歌手,姓别等条件分类的。那全部歌曲是按无条件分类的,它们也不存在什么继承关系。
T单曲=class(T歌曲) 我主要是想歌曲包含的是大类的歌曲,而单曲则是这大类里面的一个。
“现实的包含关系并不对应类的层次”?
接着写啊 收获很大,现在才知道OO原来是这样,呵呵
期待中。
ZyxIp(绝望中...) wdong18(东东) FrameSniper(§绕瀑游龙§)
你们的帖子也很精彩,受益非浅
尽管看上去点歌确实简单,好像不值得做似的。但是它是核心。它才是最重要的。
因为要做的是“点”歌。而不是“管”歌,不是歌库,更不是点歌按钮……
如果从界面,从歌曲入手,那就出现实质性的失误,他们仅仅是细节,
尽管从技术含量来说好像比点歌来的困难,但是要分清主次。点,是通俗的说法,也就是说作出一种选择。当东西多了的时候,选择还是比较困难的。但是我们选择之后要做什么呢?播放,对,实质其实还是播放歌曲。所以我在点歌上面写下了,播放。哦,好了,我们所有的工作其实是为了播放而作的。所以,我要建立一个抽象的基类,我在点歌左边写下了TChooseSongs
哦,他的第一个过程一定是个public的,因为它就是最重要的播放。
Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;为什么是虚函数后面会解释。
哦,文件名应该是被保护的,所以FFileName应该在private里面所以简单的基类建立了。如果有其他的功能扩充再说。TChooseSongs = class
private
FFileName: string;
public
Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;
end;哦?如何访问FFileName呢?嗯,对了,我们需要一个方法,一个函数来传递FFileName。OK,我们就用Template Method来建立这个类,去得到FFileName。还需要一个过程来得到FFileName,不能直接来用。TChooseSongs = class
private
FFileName: string;
protected
procedure DoLoadFile(FileName: string); Virtual; abstract;
private
Procedure LoadFile(FileName: string);
Procedure GetFileName; string;
Procedure PlaySongs(FileName: String);Boolean; virtual; abstract;
end;template mothed就是如此了,很简单的LoadFile的代码实现。Procedure TChooseSongsLoadFile(FileName: string);
begin
FFileName := FileName; //把得到的放到他该去的地方
if FileName <> '' then
begin
DoLoadFile(FileName);
end;
end;Procedure TChooseSongs.GetFileName; string;
begin
Result := FFileName;
end;
各种歌曲文件的格式迥异,无法一下子说明表达。Mepg4的,DVD的,MP3的,real格式的,还有微软发布的一些格式。等等等。这个真得有点麻烦,而且还有其他的麻烦,比如说管理方面。嘿嘿,不怕,我们有派生继承这些,然后具体实现。假设是DVD点歌那很简单,就是TChooseDVDSongs = Class(TChooseSongs)
protected
DoLoadFile(FileName: string); override;
public
procedure PlaySongs(FileName: string): boolean; override;
end;然后,实现具体的有关DVD形式歌曲的的播放,如果有其他的就扩展加入。嗯,好了,到现在,实现了播放。但是,还有选择呢。嘿嘿,基类、派生类都在不断的完善,上面写的仅仅是个初稿。既然叫choose song仅仅有Play song那还不叫choose,既然叫选择,那就明确目标,问问自己:选择什么?选择歌曲。
为什么要选择?播放呗。
如何作出选择? …… 哈哈,问到点子上了,好,关键是如何作出选择。歌曲多着呢,管理困难吗?不困难,我们有强大的数据库,合理的子目录。在乎这个?我说:NO!上面所说的给歌曲加上类?T歌曲 = class?我不敢用……哈,不敢苟同,不是什么事情都需要类的,那会效率低下何必呢?建好数据库来管理,是很轻松的事情。=================================================================================
我真的有认真看你回复的贴子,只是我太笨了,还请谅解,我觉得对点歌系统这个业务来说,你和ZyxIp(绝望中...) 对于类的划分思想大致是一样的吧?(有不同吗?能指出来吗?) 我只是觉得我 T歌曲=class
T单曲=class(T歌曲) 这样划分为什么不好哦?就是这点不明白。我这样分是不是有违面向对象的观点?还是可以这样分只是多余?我觉得T歌曲类做T歌曲类做的事,T单曲类做T单曲类做的事。还是我对面向对象的观点又错了?(或许你们已经回答了这些问题,但是能不能再通俗一点的讲一下?因为,我比较笨) ZyxIp(绝望中...) 说:
分类只是一个索引一样的东西,T单曲才是最终是操作的实体。单曲的存在与分类,如何分类并没有什么关系,所以T单曲和T分类歌曲根本就不是一类,并不存在什么继承关系
=================================================================================
我觉得T歌曲类并不仅仅是一个索引一样的东西哇,它有好多事要做呢。T单曲是T歌曲众多歌曲里面的一首歌曲哇?对于单曲来讲必须要做自已独特的事情(播放暂停等等),为什么不存在什么继承关系呢?
你上面的TChooseDVDSongs = Class(TChooseSongs)也看了,我觉得要只是放出声的话分到单曲也行了,用一个Play放所有类型
Case SongStyle of
DVD:;
VCD:;
MP3:;
.....
end;
如果每一种格式不只是Play 的方式不同,还有其它的更多的不同的可以将这个类分的更细一些,但也要充分利用多态性。
To PrettyMurphy(土豆) 你的
T歌曲=class
T单曲=class(T歌曲) 我觉得还是不对头,你的 T单曲 并不是 T歌曲 的扩展,根本就不是继承关系,现实的这种“包含”并不对应类的层次 ,面向对象中“归类”是重要步骤,类描述了具有相似性质的一组对象。 你的 T歌曲=class 只是一个单曲集合,如下定义会更明白一些。 T歌曲Item=Class
...
end;
如有必要可能给每个类型的歌定义一个类
TMP3歌曲=Class(T歌曲Item)
...
end;
TDVD歌曲=Class(T歌曲Item)
...
end;下面是管理所有歌的类
T歌曲Items=Class
Private
FItems:Array of T歌曲Item;
public
property Items[Index:integer]: T歌曲Item +Ctrl_Shift_c;
end;
<按歌手分>
<歌手1>
<第1首 名字="给用户显示的名称" 位置="路径或数据库中记录的编程">
<第2首 名字="" 位置="">
</歌手1>
<歌手2>
<第1首 名字="" 位置="">
<第2首 名字="" 位置="">
</歌手2>
<歌手3>
<第1首 名字="" 位置="">
<第2首 名字="" 位置="">
</歌手3>
</按歌手分>
<按字数分>
<1字歌>
<第1首 名字="" 位置="">
<第2首 名字="" 位置="">
</1字歌>
<2字歌>
<第1首 名字="" 位置="">
<第2首 名字="" 位置="">
</2字歌>
<3字歌>
<第1首 名字="" 位置="">
<第2首 名字="" 位置="">
</3字歌></按字数分>
................
这样是冗于,但比你每次到数据库中查要快,而且就算你有10万首歌给用户提供10种分类方式,你查找时先在10个分类中找出对应的分类,然后将下面的数据顺序显示就可以了。
点歌系统太小了,我看根本体现不出面向对象编程的好处,它的难点在于技术上如何调用多种解码器来显示。在系统结构分析上就没有什么东西。
但是你这种做法合适吗?效率有吗?我不知道你用类管理歌曲有什么意义。我看不出任何意义,你分一个目录,然后用getdir找到它,然后把文件加载到播放器就可以了。甚至可以把建立好的路径放到数据库里面,然后通过检索找到合适的。
====================================================================
分类的结果是必须要显示出来的。因为用户只会去选择分类歌曲中的一首歌曲。
而且为了方便以后维护,每个歌曲文件不会固定存放哪个目录,只需与数据库中路径相对应即可。
用getdir找就等于在数据库里找。找出来的结果RECORDSET是必须要显示给用户看的。
这样的话,分类找记录的函数或过程应该放在哪里?做成公用模块?那样也可以完全不要创建类哇。既然你的目的是点歌,那你就说点歌。可是你到现在都没有作任何点歌的措施。
==================================================================
我想,点歌的方法应该在“T单曲类”里面的方法里得到体现。包括播放及其它操作。to ZyxIp(绝望中...) 我觉得还是不对头,你的 T单曲 并不是 T歌曲 的扩展,根本就不是继承关系,现实的这种“包含”并不对应类的层次 ,面向对象中“归类”是重要步骤,类描述了具有相似性质的一组对象。
===============================================================================
我想我是错了,ZyxIp(绝望中...) 说的对,T歌曲 只是单曲的组合已经,不是继承。但是又回到了同to reallike(认真学习Cpp用Cpp考虑delphi) 一样的结论?找出来的结果RECORDSET是必须要显示给用户看的。
这样的话,分类找记录的函数或过程应该放在哪里?做成公用模块?
我只是针对楼顶的对项目的分析的一些不恰当做法提出我的一些看法。面向对象,首先就是考虑面向的“对象”是什么,自己面向的对象都不明确,怎么在一个项目里面有针对性地作为?重点不明确,没有核心,会造成一种混乱。把对象树立好了,再谈面向。如果对象树立不正确,再谈面向同样是空谈。围绕对象来操作,才是面向对象的一个本质。之后才是使用软件工程之类的技巧和经验。对于这个问题,我打个比方,打个大一点的比方。可能FS从来没有想,但是我想了。我们的人生,我们的人生所要面向的对象,也就是我们的人生目标你明确了没有?人生目标明确再说逐步实施的细节,里面可能有坎坷,可能有幸福。但是都是细节都是为我们的目标而服务的。但是大前提,就是找到对象。再打一个比方,最近FS失恋,我就拿这个来打比方。FS你是否考虑你的对象——嘿嘿,还真是对象——她到底是什么形象?什么素质?你有没有目标,也就是对象?你是否真地树立了合理的对象?假如你树立好了,中间任何不择手段的方法,仅仅是手段。:〉好,咱们回到他的点歌系统,既然是点歌系统,那就要从点歌入手。尽管看上去点歌确实简单,好像不值得做似的。但是它是核心。它才是最重要的。因为要做的是“点”歌。而不是“管”歌,不是歌库,更不是点歌按钮……如果从界面,从歌曲入手,那就出现实质性的失误,他们仅仅是细节,尽管从技术含量来说好像比点歌来的困难,但是要分清主次。好了,明确目标我再和FS讨论具体的实现。
既然没有人看,那也就算了。