Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.11.02;
Скачать: CL | DM;

Вниз

Как дисэйблить кнопки если операция не поддерживается&#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]

> и фсе&#133 теперь можно определять поддержку удаления поддержкой
> интерфейса

Я примерно понял. Только ты, плз пример напиши&#133
Нопонятно кто должен проверять поддержку и дисейблить&#133


 
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] по идее не надо будет ничего перопределять&#133

ЗЫ
 Я же поэтому и прошу примерчик, а то непонятно&#133


 
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

Возится с каждой командой долго выходит&#133


 
Kolan ©   (2008-01-25 16:37) [15]

> function OpsVisitor(&#133)  <&#151; сюда твоего Menu Manager

Ну и что я буду говорить этому менеджеру? Задисейбли кнопку &laquo;Удалить&raquo;. А если кнопок много? Что же их все пересчислять?

Паладин действительно прав. Получается я контракт нарущил. Говорю что оддерживаю удаление, а на самом деле нет&#133 Осталось грамотно связать&#133


 
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)

Не, не понял&#133 Как устроен MenuManager непонял.


> Реализатор IItemsOperationsController пробегает по всем
> его коммандам и делает вызов

Что за команды?


> ADDITEM.VISIBLE:=TRUE;

Какое-то жесткое присвоение&#133

Нихрена не понял короче&#133


 
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)

Смотрю&#133


 
Kolan ©   (2008-01-26 12:05) [24]

Понял, таки это решение с командами. А хотелось бы от них избавится.

Благодарю за обсужение. Пользу извлек такую &#151; если ты не пддерживаешь интерфейс, то нехрен говорить, что поддреживаешь.

:)


 
Игорь Шевченко ©   (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]

> получилось следующее:

Просто и понятно.


> Теперь бы делал иначе.

А как? если не секрет? Оч. интересно&#133
Хотелось бы, чтобы дабавлять команды было просто, дисэбл делался автоматически и код был простым&#133


> Keep It Simple, Stupid

В смысле Short and Simple ? :)


 
Kolan ©   (2008-01-27 09:53) [27]

> actFind.Enabled := ctFind in Supported;
> actGoto.Enabled := ctFind in Supported;

Вот это только плохой кусок, тут придется добавлять вручную&#133


 
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]

Надо подумать&#133


 
Игорь Шевченко ©   (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]

> честно бы их опрашивал

Что это значит? То есть кнопки бы всегда были активны?


> Напоследок немножно помедитируй над цитатой:

Медитирую&#133


 
Игорь Шевченко ©   (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;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.017 c
3-1208751937
piople
2008-04-21 08:25
2008.11.02
SKIP записей при селективном запросе


15-1221146089
@!!ex
2008-09-11 19:14
2008.11.02
Как заменить кадры видео с помощью VirtualDub?


15-1220277220
oldman
2008-09-01 17:53
2008.11.02
Предупреждение "начинающим"


1-1201761034
mix
2008-01-31 09:30
2008.11.02
Вопрос по Drag&amp;Dock


15-1220722887
AlexDan
2008-09-06 21:41
2008.11.02
Экранная клавиатура..