关于程序如何设计的问题?

kwer 2008-01-02 04:56:51
做企业开发有一两年了,一般的问题可以说得心应手基本都能解决,一时不能解决的翻一下google baidu 基本也能解决。

程序代码中虽有部分使用了继承,但是SQL语句满天飞,业务逻辑都写在ButtonOnClick()里,恐怕只有自己才能看懂 ^_^

网上查了一下 MVC 设计方法,不是资料少得可怜就是不太令人满意。

高手是怎么写 灵活应变又易维护清晰的代码 的?




...全文
488 52 打赏 收藏 转发到动态 举报
写回复
用AI写文章
52 条回复
切换为时间正序
请发表友善的回复…
发表回复
b13272012771 2011-06-06
  • 打赏
  • 举报
回复
挖一挖!
kwer 2008-02-04
  • 打赏
  • 举报
回复
我是楼主感谢 4楼的sdzeng(大头鸟),19楼的Corn1(玉米),47楼的xyjdn(项有建),还有其它的同行。

现在慢慢有一种感觉就是SQL语句要尽量远离界面靠近database才好,至于怎么安排怎么好,放在database里也好,写成procedure也好都可以。

其它办法如设计模式也是解决问题的一种途径,不过我不太看好。总之继承,组件,函数复用都是精简代码的好办法。
ZhuoJiao 2008-02-03
  • 打赏
  • 举报
回复
nippycn 2008-02-02
  • 打赏
  • 举报
回复
建议还是封装成业务类,所有的处理都单独一个过程,如果类似的可以共用一个,仅仅相差一两个参数可以设默认值,这样清楚。
masterjames 2008-02-02
  • 打赏
  • 举报
回复
建议看看DELPHI 源代码,学学设计模式
xyjdn 2008-01-29
  • 打赏
  • 举报
回复
原则上说:
凡是有规律性的东西,就可以通过编程解决,
反过来说,同样成立:只要是程序,必定有其规律性。
方案设计,应该多从框架流程图进行考量,找出最合理的框架流程图,然后想办法解决运用此方案而引发的技术难点。

如:

xxmmmx(踢踏) 的方案可行性很好,但是我的参数个数是不固定的,比如查询时[日期,客户,品名]选择三个的任意组合。

所以最后在ButtonOnClick()里动态生成SQL,然后才提交上去,一旦你把Sql模板放在数据库中意味参数个数也定死了。

当然从技术上说,你可以把他做成静态的。
好坏无绝对,有于你如何衡量与取舍。
xyjdn 2008-01-29
  • 打赏
  • 举报
回复
http://www.itisedu.com/phrase/200603061631585.html

设计模式

这文章写得很不错。
hawk_e2e 2008-01-29
  • 打赏
  • 举报
回复
深刻理解问题,
设计突出 简洁、高效、实用。

OK!
bridge05 2008-01-27
  • 打赏
  • 举报
回复
mark 有空再慢慢看
DONKEY_1 2008-01-26
  • 打赏
  • 举报
回复
包括窗体一起封装在DLL里(虽然delphi不支持这样做),ADO连接参数传过去..........模块化........
pengxuan 2008-01-26
  • 打赏
  • 举报
回复
学习
ZhuoJiao 2008-01-26
  • 打赏
  • 举报
回复
学习!
这是我在网上找的一篇关于Delphi面向对象的文章,希望大家发表些看法!:
链接为:http://www.itepub.net/html/kaifawendang/Delphiyuyan/bianchengjiqiao/2006/0429/7199.html

封装了商业规则的类是真正面向对象编程的基础

这篇文章我们会涉及程序设计的各个方面,并对质疑一些我们写Delphi程序的惯用方式。这些设计方法背后的基础概念是封装:设计一组清楚定义接口(方法)类,由这些方法去操作他们的属性。这一概念将会贯串整个程序并对数据如何保存和呈现有很大影响。我愿意介绍读者学习Francis Glassborow's关于C++的文章,尽管语言不同,但是优秀的类设计理念是和语言无关的。

现今大部分Delphi写的程序都不是面向对象的。只是语言中有对象的模型并使用了原有的或新的类,这并不意味者程序是真正的面向对象。代码的重用随着第三方控件被拖到窗口上而结束,窗口和单元之间的相互依赖却在迅速扩散。(!!!)如果将来要改变程序的基础(如切换不同数据库或从两层结构变成三层结构)会严重受阻或花费昂贵。如果是真正按面向对象的方式写的程序则会很方便而不是受限制。当然要写这样的程序需要理念的提升,而且开始的时候缺乏生产力,大部分开发团队都不情愿这样或作考虑。我希望通过这篇文章向大家示范如何能写出更好的程序。最终使系统更可靠,容易维护,风格一致,灵活,可重用,比用传统方式写的程序运行的更好。特别是对大型的程序,代码清晰并真正面向对象的程序会比传统方式写的同样程序需要更少的维护资源。

面向对象的程序更高的可靠性来自于数据和操作被封装在明确定义的类中。编译器通过强大的类型检查促使代码中正确的类、方法和属性,对未来一个改动会影响整个程序的代码不应让人有误解代码意图的可能。正确使用类会是类之间的关系是自明的,并且大部分的代码真正关注程序的关键部分(meat),而不是考虑像数据如何持久存储这样的细节问题。贯串代码的简单性和一致性将使程序的维护性显著提高。正如我们将看到的一样,广泛使用类继承增加了生产力和可靠性,并且增强了一致性。这些一致性体现在所展示的代码中,包括类的行为、数据如何存储和用户界面如何呈现数据。由于大部分功能在基类中提供,可以通过快速改变他们的行为来从根本上改变程序。(如用户交互界面从窗口驱动形式改为以html为基础)这些基类可以设计为与程序无关,这样第二个类似的程序将在生产力上立即得到推动。对一个中等的程序一组优秀的基类可以提供高达%50的显著提升,从时间,开支到可靠性。首先要强调的是切换到真正的面向对象开发并非琐碎小事,第一次应保证有丰富经验的协助,或者程序较小且没有紧急的交付期限;还要说明的是一个面向对象(OO)的解决方案并不是规定在程序中哪些类该用哪些类不该用。如果一家公司开发了自己的可视化控件,或者使用第三方的可视化控件,


核心类

设计任何面向对象程序的第一步是考虑哪些必须的类。这是绝对基本的一步,要有其他各种开发的技术保证,因为早期阶段的错误要改正将花费昂贵。在设计我们的类时我们一般努力实现低耦合高内聚-类与类之间尽量地独立,但又可以通过某种强大地方式复合。实现这一目标地一个方式是把类按照在它们程序中所扮演地不同角色来划分成不同地类别。对这些角色的正确选择将会形成一组内聚的类。

在对类的角色设计中有一个贯串始终的基本原则:把类的责任分为表现、应用和持久化存储数据(典型地在数据库中)。虽然这和三层数据库程序地划分是一样的,要提示的是这种划分的概念可以在多种环境中实现:从单片机程序到分布式多层程序。组成应用逻辑的这一组类负责最为困难的工作,例如响应用户操作和处理数据的请求。这一层类中有一部分类表现了真实世界的实体,并被系统所模型化。这些类常被称为“商业类”或“问题域类”。它们构成任何面向对象程序至关重要的部分,因为其它的类将通过某种方式支持这些类,它们成为所有开发者关注的焦点。

识别一个特定程序中有哪些商业对象一般是经验的本能,虽然这过程里面有一个完整的学科(或是艺术?)面向对象与传统技术如SSADM(1)相比的优点贯串于整个分析设计和维持实体的过程:可以通过面向对象分析(OOA)、面向对象设计(OOD)和面向对象编程(OOP)来表现每个商业对象。我们会在后面探寻识别合适的商业对象的部分技巧。首先我们假设下面这些过程已经完成。

不同层(表现、应用和持久)间的类的互相通信已经清楚定义并且相互连接。这些类的关系如图1




图中的箭头显示同一个层中的类可以调用另一个类中的方法。这张图同时说明表现层(用户界面)的类可以操作应用层或用户界面中的其他类,应用层(商业对象)可以操作应用层的类和调用持久层的方法,持久层只回应应用层的请求。


商业对象

为了演示商业对象是如何实现的,我们接下来将看到一个简化的程序,其中有库存,客户和订单实体(某个公司提供一定数量的货物,客户来买这些货物)。我们最初的分析确定需要三个商业对象表现这些实体。在我们的Delphi程序中将有三个类:TStockItem, TCustomer 和Torder。清单1中显示了这些类的公共接口。这里需要指出这些类的特性是通过属性(Property)暴露给外部的:这是一个简单而有益的类设计并且通过属性控制外部的访问。还有就是这些属性应该在类的published区域(原因会在后面提到),并被赋给合适的默认default值。虽然这些属性限定只在流化类时被用到,会给每个属性合适的初始值并使代码更具自描述性。


第一个类框架

在开发程序的初始阶段,我们已经识别并实现了三个商业对象。这三个对象扮演共同的角色:通过某种方式表现真实世界的实体。因此我们希望它们和真实世界的实体有相似的属性和适当的层次。我们将给每个对象一个共同的基类,叫做TPDObject(Problem Domain object)。随着时间推移我们会在这个类里加入公共的方法和属性,TPDObject将会不断得到扩展。需要注意的是TPDObject不要(永远不要)有任何跟特定程序有关的元素。作为一个程序无关的类它将放在一个独立的单元并形成框架的基础:一组可以在许多程序中重用的类。在后面我们的框架会有很大扩展,形成提供重要的程序无关功能的基类,并可以被迅速用于特定的系统。我们TStockItem、TCustomer 和 Torder就是从TPDObject为特定系统定制对象。

TMyAppPDObject是TPDObject的一个继承类,虽然它的实现部分是空的,但是它是一个很好的示范;如果我们为在特定程序加某些特定元素,并影响程序中所有的问题域类,这样的类层次应该是比较合适的。清单中列出了框架的初期代码。类TPDObject还只提供一个只读属性ID,它的类型是TobjectID。这个属性给了每个对象一个标识符:我们将把相同类型和ID的两个实例认为是同样的对象。这里ID被故意声明为自己的类型是为了避免被直接赋给其它标准类型的值。TobjectID的类型并没有经过特别的选择:在这里我选择了整形,在特定情况下它也可能是一个专门的类。虽然用类作ID好像是更纯的面相对象,但基于我们的问题域类在程序运行中会被创建、释放上千次考虑,为了避免创建和释放对象时额外的负荷,我并没有这样做(作为纯粹主义论者我把TobjectID包含在TPDObject中作为复合的对象)。在代码中有一对函数用于转换字符串和我们的对象标识类型,并且有一个常量作为没有赋值时的初始值。有很多课堂会提到对象标识:有的说所有的商业对象都应该在创建时被赋予一个程序内不重复的标识(甚至是GUID)。实际上,能在给定的上下文中区分不同的对象才是真正重要的,并且保持这一简单性将会在性能和存储空间上有明显的好处。

关于规范的问题:

为了强化一些设计的理念和设计类框架时的做法我将提一些问题,请读者思考它们后面的基本原理是什么。我曾提到对于真正的面向对象设计并不禁止使用特定的类,但有一个很重要的例外。这个例外是什么(答案在图1中)

((( Listing 1 - An application-specific Problem Domain unit (abridged) )))

unit ProblemDomain;


interface


uses

Framework;


type

TMyAppPDObject = class (TPDObject)

end;


TStockItem = class (TMyAppPDObject)

published

property Name: String;

property QuantityInStock: Cardinal default 0;

property TradePrice: Currency;

property RetailPrice: Currency;

end;


TCustomer = class (TMyAppPDObject) … ;


TOrder = class (TMyAppPDObject) … ;


implementation


end.

((( End Listing 1 )))


((( Listing 2 - An application-independent Framework unit )))

unit Framework;


interface


const

NotAssigned = 0;


type

TObjectID = type Integer;


TPDObject = class

private

FID: TObjectID;

public

property ID: TObjectID read FID default NotAssigned;

end;


function StrToID (Value: String): TObjectID;

function IDToStr (Value: TObjectID): String;


implementation





end.

((( End Listing 2 )))



(1) SSADM(Structured Systems Analysis & Systems Design)是1981年英国政府的中央电脑及电讯中心 (Central Computer and Telecommunications Agency ,简 称 CCTA ; 网 站 :http://www.ccta.gov.uk )研制的软件分析设计标准方法。

zhouchunyu 2008-01-16
  • 打赏
  • 举报
回复
帮顶
ZyxIp 2008-01-16
  • 打赏
  • 举报
回复
将数据库访问封装.所有提交到到数据库的查询都做为一个任务,保存在任务队列中,创建一个线程池,从任务队列中取数据进行操作.完成后用POSTMESSAGE发送消息通知完成.如果将所有要执行的SQL都保存在一个外部的文件中,看成一个个的SQL语句块,起一个方法的名称,则在调用时只要给定方法名称就可以了.根据不同的数据库加载不同的脚本.

业务层的方法调用数据层

大部分的业务对象都是属于列表和元素的关系,或者是树节点的关系,所以先实现一个自己的List ,
实现ToListView(ListView:TListView) ToList(List:TStrins) 这样的方法
元素实现 ToListItem(ListItem:TListItem) 这样能实现大多数的界面显示

窗体继承总是觉的不太好,一个的修改会影响太多.针对不同的业务实现独立的窗体也更容易接口化.

业务对象一般也是如下这些方法

TA=class(元素)
public
procedure Delete;
function Attribute:Boolean;
function Save:Boolean;
end;

TAList=class(列表)
protected
procedure LoadFromDB;
public
function Add:Integer;
procedure Refresh;
property Items[Index:Integer]:TA read GetItems;
end;

//显示业务A
TWinAList=class
private
FAList:TAList;
FForm:TForm;
public
constructor Create(AList:TAList);
function Show;
function ShowModal:Integer;
procedure Close;
published
property AList:TAListread FAList;
end;

function TWinAList.ShowModal:Integer;
begin
FForm:=Tfrm_AList.Create;
Tfrm_AList(FForm).WinAList :=Self;
FForm.ShowModal;
Result:=Form.ModalResult;
FreeAndNil(FForm);
end;

前台为每一个业务设计业务对象如 TA TB TC 业务对象的显示用
Tfrm_AList=class(Tform)

published
property WinAList :TWinAList read FWinAList write SetWinAList ;
end;
的方式处理.

用 TWinAList 对象进行显示的管理,也是为了能将显示界面接口化,将业务对象和窗体隔离开来,让程序能一个"整齐"的实现方式.
kugoo_2006 2008-01-16
  • 打赏
  • 举报
回复
我现在的做法是:
把sql 语句写进表里边,把常用功能封装起来,写配置文件,报表用word
所以程序出来后,很灵活。客户有需求变动,不需要怎么改程序
ljlsh 2008-01-10
  • 打赏
  • 举报
回复
还是多学学设计模式方面的知识,才是最好的答案。
才子鸣 2008-01-10
  • 打赏
  • 举报
回复
其实同意xxmmmx 的说法。

大多数窗体用多重继承。
所有SQL语句写在一个公用数据单元。
业务逻辑分开!
onemonth 2008-01-10
  • 打赏
  • 举报
回复
一个方案的简略表示
TFieldType = (Int, String, ......);
TFieldMap = class
FType:TFieldType;
FSize:integer;
FName:string;
......
end;

TTableMap = class
FName:string;
......
end;

TRelationType = (Master, Detail......);


TRelationRunner = class
public
procedure Accept(tableMap:TTableMap); abstract;
end;


TTableRelation = class
public
procedure AddTable(node:TTableMap);
procedure AddRelation(node1, node2:TTableMap, relation:TRelationType);
procedure Run(runner:TRelationRunner);
......
end;

TSQLField = class
FName:string;
FAlias:string;
end;

TSQLTable = class
public
constructor Create(name, alias:string);
......
end;

TSQLFunction = class
public
function GetExpression():string;
......
end;

TSQLQuery = class
public
function Select(field:string);
......

procedure AddWhere(...);
......

fucntion GetExpression():string;
......
end;


TValue=class
FFieldMap:TFieldMap;
FValue:variant;
public
procedure SetValue(value:variant);
function GetValue():variant;
......
end;


TObjectBase=class
FTableMap:TTableMap;
FValues:TList; // TList(TValue);
public
function GetValue(index:integer):TValue;
function GetValue(name:string):TValue;
......
end;

TObjectWin=class
FQuery:SQLQuery;
public
procedure GetQuery():SQLQuery;
......
end;


TObjectFactory=class
FRelation:TTableRelation;
public
procedure InitObject(obj:TObjectBase, objWin:TObjectWin);
procedure InitObject(obj:TObjectBase, idValue:variant);
procedure Insert(obj:TObjectBase);
procedure Modify(obj:TObjectBase);
procedure Delete(obj:TObjectBase);
procedure Save(obj:TObjectBase);
......
end;

业务模型,内部采用上面东东
TMode = class
public

end;

// 控制器,处理Mode,View的通信,View直接用 Delphi提供的控件
TPreset = class
public
procedure SetControl(ctrl:TControl);
procedure SetControl(ctrl:TObject);
procedure SetMode(mode:TMode);
procedure BindData(...);
procedure BindEvent(...);
procedure SetData(...);
function Run():boolean;
end;


// 泛化工作流
TFlow = class
public
function Run():boolean;abstract;
end


TFormFlow = class(TFlow)
FSubFlow:TFlow;
public
function Run():boolena;
end;

TEventFlow = class(TFlow)
end;
......
kwer 2008-01-06
  • 打赏
  • 举报
回复
polly_110

...界面设计好了,写代码时,就别想着,在哪个 按钮 下, 或是某个控件双击或单击,该写哪些SQL语句,从哪些表里读取数据,然后,点"保存"时,去更新哪些表,,这样的代码很难维护.不要习惯性地在Form的OnCreate 连接数据库,在某个Button下查询数据库,然后在某个button里,保存数据,...

------
那又该怎么做啊?
kwer 2008-01-06
  • 打赏
  • 举报
回复
TO_LS
你所提到的我都做到了 ^_^
加载更多回复(32)

2,497

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 数据库相关
社区管理员
  • 数据库相关社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧