假设您有一个菜单项和按钮,它们执行相同的任务。为什么在一个控件的动作事件中放置任务代码,然后从另一个控件调用该事件是不好的做法呢?Delphi和VB6允许这样做,但Realbasic不允许,并建议将代码放入一个方法中,然后通过该方法调用菜单和按钮。
假设您有一个菜单项和按钮,它们执行相同的任务。为什么在一个控件的动作事件中放置任务代码,然后从另一个控件调用该事件是不好的做法呢?Delphi和VB6允许这样做,但Realbasic不允许,并建议将代码放入一个方法中,然后通过该方法调用菜单和按钮。
这取决于你程序的组织方式。在你所描述的情境中,菜单项的行为将根据按钮的行为来定义:
procedure TJbForm.MenuItem1Click(Sender: TObject);
begin
// Three different ways to write this, with subtly different
// ways to interpret it:
Button1Click(Sender);
// 1. "Call some other function. The name suggests it's the
// function that also handles button clicks."
Button1.OnClick(Sender);
// 2. "Call whatever method we call when the button gets clicked."
// (And hope the property isn't nil!)
Button1.Click;
// 3. "Pretend the button was clicked."
end;
One is to get rid of the MenuItem1Click
method altogether and assign the Button1Click
method to the MenuItem1.OnClick
event property. It's confusing to have methods named for buttons assigned to menu items' events, so you'll want to rename the event handler, but that's OK, because unlike VB, Delphi's method names do not define what events they handle. You can assign any method to any event handler as long as the signatures match. Both components' OnClick
events are of type TNotifyEvent
, so they can share a single implementation. Name methods for what they do, not what they belong to.
Another way is to move the button's event-handler code into a separate method, and then call that method from both components' event handlers:
procedure HandleClick;
begin
// Do something.
end;
procedure TJbForm.Button1Click(Sender: TObject);
begin
HandleClick;
end;
procedure TJbForm.MenuItem1Click(Sender: TObject);
begin
HandleClick;
end;
This way, the code that really does stuff isn't tied directly to either component, and that gives you the freedom to change those controls more easily, such as by renaming them, or replacing them with different controls. Separating the code from the component leads us to the third way:
The TAction
component, introduced in Delphi 4, is designed especially for the situation you've described, where there are multiple UI paths to the same command. (Other languages and development environments provide similar concepts; it's not unique to Delphi.) Put your event-handling code in the TAction
's OnExecute
event handler, and then assign that action to the Action
property of both the button and the menu item.
procedure TJbForm.Action1Click(Sender: TObject);
begin
// Do something
// (Depending on how closely this event's behavior is tied to
// manipulating the rest of the UI controls, it might make
// sense to keep the HandleClick function I mentioned above.)
end;
Want to add another UI element that acts like the button? No problem. Add it, set its Action
property, and you're finished. No need to write more code to make the new control look and act like the old one. You've already written that code once.
TAction
goes beyond just event handlers. It lets you ensure that your UI controls have uniform property settings, including captions, hints, visibility, enabledness, and icons. When a command isn't valid at the time, set the action's Enabled
property accordingly, and any linked controls will automatically get disabled. No need to worry about a command being disabled through the tool bar, but still enabled through the menu, for example. You can even use the action's OnUpdate
event so that the action can update itself based on current conditions, instead of you needing to know whenever something happens that might require you to set the Enabled
property right away.
你应该将内部逻辑分离到其他函数中并调用此函数...
这是一种更优雅的解决方案,也更容易维护。
关注点分离。 对于一个类的私有事件,应该封装在该类中,而不是从外部类调用。这样,如果对象之间具有强接口并且最小化多个入口点的发生,则可以使您的项目更轻松地进行更改。
显然,这样更整洁。但是易用性和生产力当然也非常重要。
在Delphi中,我通常不在严肃的应用程序中使用它,但在小型项目中调用事件处理程序。如果小项目变成了更大的项目,我会进行清理,并通常同时增加逻辑-UI分离。
我知道在Lazarus/Delphi中这并不重要。其他语言可能会附加更多特殊行为到事件处理程序。
为什么这样做是不好的实践?因为如果代码嵌入到UI控件中,那么重用代码就会变得更加困难。
为什么在REALbasic中不能这样做呢?我怀疑没有任何技术原因,这很可能只是他们做出的设计决策。这确实可以执行更好的编码实践。