Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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]

> и фсе&#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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.008 c
2-1222116408
ShyricK
2008-09-23 00:46
2008.11.02
Вопрос по функционированию ЭВМ (дешифратор)


1-1201174663
Elec3C
2008-01-24 14:37
2008.11.02
Свойства файла


2-1221909236
MZG
2008-09-20 15:13
2008.11.02
Как добавить записи поля из базы?


2-1222447265
AlexDan
2008-09-26 20:41
2008.11.02
Форма..


2-1222067106
Ivanov Ivan
2008-09-22 11:05
2008.11.02
Сравнить элементы





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский