Форум: "Основная";
Текущий архив: 2008.11.02;
Скачать: [xml.tar.bz2];
ВнизКак дисэйблить кнопки если операция не поддерживается&#133 ? Найти похожие ветки
← →
Kolan © (2008-01-25 15:32) [0]Здравствуйте,
Допустим у нас есть два вида объектов А и Б.
С ними можно выполнять похожие по смыслы(интерфейсу) операции, только реализованы они будут по разному.
Понятно, что удобно узать стратегии. Тогда надо определить общий интерфейс, и сделать его конкретный реализации для А и Б.
Пример:IItemsOperationsController = interface
["{3574BBB8-1413-4508-AD48-F4BEFFC88A28}"]
procedure AddItem;
procedure DeleteCurrentItem;
procedure SetCurrentItem(AnObject: TObject);
procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
IDeviceOperationPerformer);
end;
TCustomItemsOperationsController = class(TInterfacedObject, IItemsOperationsController)
public
procedure AddItem; virtual; abstract;
procedure DeleteCurrentItem; virtual; abstract;
procedure SetCurrentItem(AnObject: TObject); virtual; abstract;
procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
IDeviceOperationPerformer); virtual; abstract;
end;
TNetItemsOperationsController = class(TCustomItemsOperationsController)
protected
function QueryNetName(var AName: string): Boolean;
public
procedure AddItem; override;
procedure DeleteCurrentItem; override;
procedure SetCurrentItem(AnObject: TObject); override;
procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
IDeviceOperationPerformer); override;
end;
TDeviceItemsOperationsController = class(TCustomItemsOperationsController)
protected
function QueryDeviceAddress(var AnAddress: Byte): Boolean;
public
procedure AddItem; override;
procedure DeleteCurrentItem; override;
procedure SetCurrentItem(AnObject: TObject); override;
procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
IDeviceOperationPerformer); override;
end;
Тогда вызов будет такой(это код экшена):procedure TMainForm.DeleteItemExecute(Sender: TObject);
begin
GetConfigurationUtilityController.DeleteCurrentItem;
end;
ГдеGetConfigurationUtilityController
— это синглетон, который делегирует вызов нужному контроллеру.procedure TConfigurationUtilityController.DeleteCurrentItem;
begin
GetItemsOperationsController.DeleteCurrentItem;
end;
Но вот проблемма, объекты типа Б не поддерживают операцию удаления(например). Для них хорошо бы кнопочку(экшен) удалить сделать недоступной.
Вопрос: Как?
← →
Palladin © (2008-01-25 15:38) [1]определяем базовый интерфейс
IItemsOperationsController = interface
procedure AddItem;
procedure DeleteCurrentItem;
procedure SetCurrentItem(AnObject: TObject);
procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
IDeviceOperationPerformer);
end;
определяем интерфейс наследник с поддержкой удаления
IItemsOperationsControllerWithDeleteSupport = interface(IItemsOperationsController)
procedure DeleteCurrentItem;
end;
и фсе... теперь можно определять поддержку удаления поддержкой интерфейса
← →
Palladin © (2008-01-25 15:39) [2]ой, в базе обшибся, там Delete не нужен
← →
Kolan © (2008-01-25 15:47) [3]> и фсе… теперь можно определять поддержку удаления поддержкой
> интерфейса
Я примерно понял. Только ты, плз пример напиши…
Нопонятно кто должен проверять поддержку и дисейблить…
← →
clickmaker © (2008-01-25 15:53) [4]IsSupported(operationName): boolean; virtual
а вызывать в Action.OnUpdate
так, что-ли?
← →
Kolan © (2008-01-25 15:57) [5]> IsSupported(operationName): boolean; virtual
Ну у кого это операция будет реализована?
В данном примере в Синглетоне (GetConfigurationUtilityController — это синглетон, который делегирует вызов нужному контроллеру.)?
Что-то вроде:procedure TMainForm.DeleteItemUpdate(Sender: TObject);
begin
(Sender as TAction).Enabled := GetConfigurationUtilityController.GetItemsOperationsController.GetInterface(IIte msOperationsControllerWithDeleteSupport, Int);
end;
?
← →
clickmaker © (2008-01-25 16:05) [6]
> [5] Kolan © (25.01.08 15:57)
что-то у тебя мудренно как-то всё...
наверно, в IItemsOperationsController и объявить
а переопределять ниже по иерархии
← →
Kolan © (2008-01-25 16:16) [7]> а переопределять ниже по иерархии
Непонял.
Как в [5] по идее не надо будет ничего перопределять…
ЗЫ
Я же поэтому и прошу примерчик, а то непонятно…
← →
clickmaker © (2008-01-25 16:19) [8]
> [7] Kolan © (25.01.08 16:16)
ну я тогда не понял твою задумку...
мне как-то старый добрый полиморфизм ближе в этом отношении
← →
Palladin © (2008-01-25 16:22) [9]
> procedure TMainForm.DeleteItemUpdate(Sender: TObject);
> begin
> (Sender as TAction).Enabled := GetConfigurationUtilityController.GetItemsOperationsController.GetInterface(IIte
> msOperationsControllerWithDeleteSupport, Int);
> end;
да, именно так
← →
offff (2008-01-25 16:27) [10]
> Но вот проблемма, объекты типа Б не поддерживают операцию
> удаления(например). Для них хорошо бы кнопочку(экшен) удалить
> сделать недоступной.
Замечания к реализации. IMHO
Тогда и интерфейс IItemsOperationsController не должен ее содержать.
Добиться можно выделив операцию в отдельную сущность.
А именно либо отдельный интерфейс, либо класс на каждую операцию.
Аля command согласно GOF.
← →
offff (2008-01-25 16:27) [11]Удалено модератором
Примечание: дубль
← →
offff (2008-01-25 16:34) [12]Либо сделать посетителя операций. и сделать посетителя Menu Manager.
IItemsOperationsController = interface
function OpsVisitor(...) <- сюда твоего Menu Manager
{
}
end
← →
offff (2008-01-25 16:34) [13]Удалено модератором
Примечание: дубль
← →
Kolan © (2008-01-25 16:34) [14]> А именно либо отдельный интерфейс
Паладин так и предлагает.
> Аля command согласно GOF
Возится с каждой командой долго выходит…
← →
Kolan © (2008-01-25 16:37) [15]> function OpsVisitor(…) <— сюда твоего Menu Manager
Ну и что я буду говорить этому менеджеру? Задисейбли кнопку «Удалить». А если кнопок много? Что же их все пересчислять?
Паладин действительно прав. Получается я контракт нарущил. Говорю что оддерживаю удаление, а на самом деле нет… Осталось грамотно связать…
← →
offff (2008-01-25 16:42) [16]
> Kolan © (25.01.08 16:37) [15]
Menu Manager - отключает все кнопки.
MenuManager=class(CommandVisitor)
procedure OpAdd
procedure OpRemove
Procedure OpCurrent
end;
Реализатор IItemsOperationsController пробегает по всем его коммандам и делает вызов
AbstractCommand.Accept(visitor:CommandVisitor)
procedure MenuManager.OpAdd
begin
ADDITEM.VISIBLE:=TRUE;
end;
procedure MenuManager.OpRemove
begin
RemoveITEM.VISIBLE:=TRUE;
end;
Procedure MenuManager.OpCurrent
begin
....
end;
← →
offff (2008-01-25 16:42) [17]Удалено модератором
Примечание: да что"ж ты дублями то :)
← →
oxffff © (2008-01-25 16:47) [18]Что то задублилось у меня. Sorry.
Что касаемо реализации то я уже тут предлагал улучшенную реализацию GOF паттерна VISITOR без привязки к VMT.
Кстати напишу в своем блоге об этом.
:)
← →
oxffff © (2008-01-25 16:49) [19]
> offff (25.01.08 16:42) [17]
> Удалено модератором
> Примечание: да что"ж ты дублями то :)
Sorry снова. Сам не знаю что за дела. Под oxffff пользователем порядок.
:)
← →
Kolan © (2008-01-25 19:36) [20]> [16] offff (25.01.08 16:42)
Не, не понял… Как устроен MenuManager непонял.
> Реализатор IItemsOperationsController пробегает по всем
> его коммандам и делает вызов
Что за команды?
> ADDITEM.VISIBLE:=TRUE;
Какое-то жесткое присвоение…
Нихрена не понял короче…
← →
oxffff © (2008-01-26 01:24) [21]
> Нихрена не понял короче…
AddCommand=class;
DeleteCommand=class;
CurrentCommand=class;
AbstractCommandVisitor=class
procedure VAdd(Command:AddCommand);virtual;abstract;
procedure VCurrent(Command:CurrentCommand);virtual;abstract;
procedure VDelete(Command:DeleteCommand);virtual;abstract;
end;
MenuBarCommandVisitor=class(AbstractCommandVisitor)
procedure VAdd(Command:AddCommand);override;
procedure VDelete(Command:DeleteCommand);override;
procedure VCurrent(Command:CurrentCommand);override;
end;
AbstractCommand=class
function Exec(const param):integer;virtual;
procedure Accept(Visitor:AbstractCommandVisitor);virtual;
end;
TAbstractCommandClass=Class of AbstractCommand;
AddCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;
DeleteCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;
CurrentCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;
DynaCommands=class
Commands:Tlist;
constructor create;overload;
constructor create(Commands:array of AbstractCommand);overload;
destructor destroy;override;
procedure Accept(Visitor:AbstractCommandVisitor);
function TryPerfomOp(CommandClass:TAbstractCommandClass;const param):boolean;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ DynaCommands }
constructor DynaCommands.create;
begin
Commands:=TList.Create;
end;
procedure DynaCommands.Accept(Visitor: AbstractCommandVisitor);
var i:integer;
begin
for i:=0 to commands.Count-1 do AbstractCommand(commands.Items[i]).Accept(Visitor);
end;
constructor DynaCommands.create(Commands: array of AbstractCommand);
var i:integer;
begin
create;
for i:=0 to length(commands)-1 do self.Commands.Add(commands[i]);
end;
destructor DynaCommands.destroy;
var i:integer;
begin
for i:=0 to commands.Count-1 do Tobject(commands.Items[i]).Free;
Commands.free;
inherited;
end;
function DynaCommands.TryPerfomOp(CommandClass:TAbstractCommandClass;const param):boolean;
var i:integer;
begin
for i:=0 to commands.count-1 do
if (AbstractCommand(commands.Items[i]) is CommandClass) then
begin
AbstractCommand(commands.Items[i]).Exec(param);
result:=true;
exit;
end;
result:=false;
end;
{ AbstractCommand }
procedure AbstractCommand.Accept(Visitor: AbstractCommandVisitor);
begin
//
end;
function AbstractCommand.Exec(const param): integer;
begin
//
end;
{ AddCommand }
procedure AddCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VAdd(self);
end;
function AddCommand.Exec(const param): integer;
begin
//
end;
{ DeleteCommand }
procedure DeleteCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VDelete(self);
end;
function DeleteCommand.Exec(const param): integer;
begin
//
end;
{ MenuBarCommandVisitor }
procedure MenuBarCommandVisitor.VAdd(Command: AddCommand);
begin
Showmessage("Enable ADD Command");
end;
procedure MenuBarCommandVisitor.VCurrent(Command: CurrentCommand);
begin
Showmessage("Enable Current Command");
end;
procedure MenuBarCommandVisitor.VDelete(Command: DeleteCommand);
begin
Showmessage("Enable Delete Command");
end;
{ CurrentCommand }
procedure CurrentCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VCurrent(self);
end;
function CurrentCommand.Exec(const param): integer;
begin
//
end;
procedure TForm1.Button1Click(Sender: TObject);
var DynaCmds:DynaCommands;
MenuBar:MenuBarCommandVisitor;
begin
try
DynaCmds:=DynaCommands.create([AddCommand.Create,CurrentCommand.Create]);
try
//Disable menu item
MenuBar:=MenuBarCommandVisitor.Create;
try
//Menu init
DynaCmds.Accept(MenuBar);
if not DynaCmds.TryPerfomOp(DeleteCommand,self) then showmessage("Not such command DeleteCommand");
if DynaCmds.TryPerfomOp(CurrentCommand,self) then showmessage("Not such command CurrentCommand")
finally
MenuBar.free;
end;
finally
DynaCmds.free;
end;
except
end;
end;
← →
oxffff © (2008-01-26 01:26) [22]
> if DynaCmds.TryPerfomOp(CurrentCommand,self) then showmessage("Not
> such command CurrentCommand")
Есть такая команда. :)
← →
Kolan © (2008-01-26 09:21) [23]> [21] oxffff © (26.01.08 01:24)
Смотрю…
← →
Kolan © (2008-01-26 12:05) [24]Понял, таки это решение с командами. А хотелось бы от них избавится.
Благодарю за обсужение. Пользу извлек такую — если ты не пддерживаешь интерфейс, то нехрен говорить, что поддреживаешь.
:)
← →
Игорь Шевченко © (2008-01-26 22:44) [25]
> Как дисэйблить кнопки если операция не поддерживается… ?
>
Button.Enabled := false;
Чего гадать-то ?
В свое время тоже маялся подобной заумью, получилось следующее:
Есть MDI-форма с тулбаром, на тулбаре кнопки, на кнопках Actions, в зависимости от допустимости операций с текущим активным MDI-дитем, кнопки должны быть открыты или закрыты.
type
TCommandType = (ctFind, ctGoto);
TCommandTypes = set of TCommandType;
IToolbarCommands = interface
["{D97A7812-091F-4E2F-84DA-08A394646BC2}"]
function SupportedCommands : TCommandTypes;
procedure FindCommand;
procedure GotoCommand;
end;
И использование:
procedure TfrmMain.ActionList1Update(Action: TBasicAction;
var Handled: Boolean);
var
Supported: TCommandTypes;
IC: IToolbarCommands;
begin
if Assigned(ActiveMDIChild) and
ActiveMDIChild.GetInterface(IToolbarCommands, IC) then
Supported := IC.SupportedCommands
else
Supported := [];
actFind.Enabled := ctFind in Supported;
actGoto.Enabled := ctFind in Supported;
end;
Теперь бы делал иначе.
> Понятно, что удобно узать стратегии.
Keep It Simple, Stupid
← →
Kolan © (2008-01-27 09:52) [26]> получилось следующее:
Просто и понятно.
> Теперь бы делал иначе.
А как? если не секрет? Оч. интересно…
Хотелось бы, чтобы дабавлять команды было просто, дисэбл делался автоматически и код был простым…
> Keep It Simple, Stupid
В смысле Short and Simple ? :)
← →
Kolan © (2008-01-27 09:53) [27]> actFind.Enabled := ctFind in Supported;
> actGoto.Enabled := ctFind in Supported;
Вот это только плохой кусок, тут придется добавлять вручную…
← →
oxffff © (2008-01-27 10:50) [28]
> Kolan © (27.01.08 09:53) [27]
> > actFind.Enabled := ctFind in Supported;
> > actGoto.Enabled := ctFind in Supported;
>
> Вот это только плохой кусок, тут придется добавлять вручную…
Рассмотри другой способ.
Команда сама предоставляет callback процедуру и/или иконку.
Твой Menu опрашивает команды на наличие этой функциональности.
И в случае отсутствия ставит произвольную иконку.
Так ты сможешь избавиться от статической привязки построителя меню к набору обрабатываемых им конанд.
+ обеспечишь подрузку внешних комманд.
← →
Kolan © (2008-01-27 11:04) [29]Надо подумать…
← →
Игорь Шевченко © (2008-01-27 12:44) [30]Kolan © (27.01.08 09:52) [26]
> А как? если не секрет? Оч. интересно…
Унаследовал бы класс формы от предка, в котором были бы методы CanFind, CanGoto (это в моем случае) и честно бы их опрашивал.
Код получился бы короче и яснее.
> В смысле Short and Simple ? :)
В смысле будь проще. Не умножай сущности сверх необходимости. Подумай что тебе важнее - иметь возможность быстро и с удовольствием прочитать исходный текст программы и понять, что она делает или городить огород с динамическими присвоениями, запутывая код нагромождением слоев.
Задай себе вопрос - что тебе чаще придется делать - читать код или дополнять его функциональность.
> Вот это только плохой кусок, тут придется добавлять вручную…
В твоем случае, как бы ты не сделал, тоже придется добавлять вручную.
Напоследок немножно помедитируй над цитатой:
"ОО-языки упрощают абстракцию, возможно, даже слишком ее упрощают. Они поддерживают создание структур с большим количеством связующего кода и сложными уровнями.
Это может оказаться полезным в случае, если предметная область является
действительно сложной и требует множества абстракций, и вместе с тем такой подход может обернуться неприятностями, если программисты реализуют простые вещи сложными способами, просто потому что им известны эти способы и они умеют ими пользоваться.
Все ОО-языки несколько сколнны "втягивать" программистов в ловушку избыточной иерархии. Чрезмерное количество уровней разрушает прозрачность: крайне затрудняется их просмотр и анализ ментальной модели, которую по существу реализует код. Всецело нарушаются правила простоты, ясности и прозрачности, а в результате код наполняется скрытыми ошибкми и создает постоянные проблемы при сопровождении.
Данная тенденция, вероятно, усугубляется тем, что множество курсов по
программированию преподают громоздкую иерархию как способ удовлетворения правила представления. С этой точки зрения множество классов приравнивается к внедрению знаний в данные. Проблема данного подхода заключается в том, что слишком часто "развитые данные" в связующих уровнях фактически не относятся у какому-либо естественному объекту в области действия программы - они предназначены только для связующего уровня.
Одной из причин того, что ОО-языки преуспели в большинстве характерных для них предметных областей (GUI-интерфейсы, моделирование, графические средства), возможно, является то, что в этих областях относительно трудно неправильно определить онтологию типов.
Например, в GUI-интерфейсах и графических средствах присутствует довольно естественное соотвествие между манипулируемыми
визуальными объектами и классами. Если выясняется, что создается большое
количество классов, которые не имеют очевидного соответствия с тем, что
происходит на экране, то, соотвественно, легко заметить, что связующий уровень стал слишком большим."
(с) Эрик Рэймонд, Искусство программирования для Unix
← →
Kolan © (2008-01-27 12:56) [31]> честно бы их опрашивал
Что это значит? То есть кнопки бы всегда были активны?
> Напоследок немножно помедитируй над цитатой:
Медитирую…
← →
Игорь Шевченко © (2008-01-27 13:27) [32]Kolan © (27.01.08 12:56) [31]
> Что это значит? То есть кнопки бы всегда были активны?
Это значит, что в обработчике события ActionListUpdate я бы смотрел, является ли текущее окно экземпляром класса, поддерживающего эти операции и если да, то опрашивал бы эти методы.
actFind.Enabled := (ActiveMDIChild is TMyClass) and TMyClass(ActiveMDIChild).CanFind
как-то так.
← →
Kolan © (2008-01-27 13:33) [33]> Это значит,
Ааа, не увидел что тут CanFind.
Понял.
Тогда на каждую операцию еще надо добавить Can операцию. Довольно просто.
Но мне кажется, что мой вариант с интерфейсам еще лучьше, бо не надо будет Can операцию добавлять и проверка попроще.
Будет так:actFind.Enabled := ActiveMDIChild.GetInterface(IFind, TheInt);
Вообщем понятно. Много вариантов получил. Благодарю за обсуждение.
← →
Игорь Шевченко © (2008-01-27 13:45) [34]
> Будет так:
> actFind.Enabled := ActiveMDIChild.GetInterface(IFind, TheInt);
>
Тогда уж:
actFind.Enabled := Supports(ActiveMDIChild, IFind);
Небольшая разница, но код становится яснее.
← →
Kolan © (2008-01-27 13:51) [35]> Небольшая разница, но код становится яснее.
Согласен.
Имхо ниче так решение особенно в сочетании со стратегиями. Можно сделать мелкие интерфейсы (IFind, IGoto), а стратегии(или MDI Children) будут реализовывать их комбинации.
Благодарю. :)
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2008.11.02;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.007 c