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

Вниз

Friend-классы   Найти похожие ветки 

 
Vladix   (2008-05-07 07:59) [0]

Всем добрый день!

Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных friend-классов?

Напихивание кода в один модуль для того, чтобы была возможность видеть поля класса, меня не устраивает, так как при этом модули разрастаются, превращаясь в кашу. А делать поля класса общедоступными тоже не хочется.

Может, есть какие-нибудь директивы компилятора, или в новых версиях Delphi что-то появилось?


 
Сергей М. ©   (2008-05-07 09:38) [1]


> или в новых версиях Delphi что-то появилось?


В новых - это в каких ? В Д7 их точно нет.

А что, без этой самой "дружественности" уж совсем никак не обойтись ? На ней прямо-таки свет клином сошелся ?


 
Хитрий Лис   (2008-05-07 09:59) [2]


> есть ли в Delphi аналоги Си-шных friend-классов

Прямых аналогов нет.


> так как при этом модули разрастаются, превращаясь в кашу


А может подойти к вопросу с другой стороны ?
1. Научиться пользоваться Class Explorer.
2. Увеличить mental power skill.

PS: D5 unit Excel2000  - 65957 строк/5 032 718 байт - каши нет :)


 
Ega23 ©   (2008-05-07 10:13) [3]

Есть интерфейсы.


 
Ins ©   (2008-05-07 10:38) [4]


> Напихивание кода в один модуль для того, чтобы была возможность
> видеть поля класса, меня не устраивает, так как при этом
> модули разрастаются, превращаясь в кашу. А делать поля класса
> общедоступными тоже не хочется.


Законный способ - размещение в одном модуле. Что-то мне кажется, что такое количество друзей у класса, что в модуле получится каша - это признак плохой архитектуры


 
Vladix   (2008-05-07 10:50) [5]


> А что, без этой самой "дружественности" уж совсем никак
> не обойтись ? На ней прямо-таки свет клином сошелся ?


Обойтись, конечно, можно.. И свет на "дружественности" клином не сошелся.. Речь идет только о том, что есть моменты, когда "дружественность" действительно была бы удобна. Я не поклонник Си, но friend-классы считаю удобной вещью для разделения функционала по разным модулям.
К примеру, есть класс, отвечающий за выполнение некоторых функций. Рядом есть иерархия классов, отвечающих за отображение данных первого класса на различные визуальные контролы. И плюс - имеется иерархия классов для сохранения данных первого класса в различные хранилища.
Так вот, все классы этих двух иерархий обязаны знать о том, как устроен первый класс.
Все сваливать в один модуль не очень хочется, а по-другому как?


> Есть интерфейсы.

Замечательно, что они есть )) и пользоваться ими я умею. Но в данном случае это мало чем помогает: все методы интерфейсов - открытые, какой тут может быть разговор об инкапсуляции?


 
Ins ©   (2008-05-07 10:55) [6]


> Но в данном случае это мало чем помогает: все методы интерфейсов
> - открытые, какой тут может быть разговор об инкапсуляции?
>

С интерфейсами просто можно поступить хитро. Закрыть нужные методы класса в приват и сделать их реализациией интерфейса. Тогда клиентский код сможет получить доступ к этим методам только запросив интерфейс. Запрос интерфейса - это будет своего рода "код доступа" и подтверждение, что клиент знает, что делает. Ну а еще уже в самом коде запроса интерфейса можно сделать некую хитрую проверку на то, имеет ли данный код право на доступ к методам.


 
Юрий Зотов ©   (2008-05-07 11:00) [7]

> Vladix   (07.05.08 07:59)  

> Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных
> friend-классов?

В некотором роде есть.

1. Загоняем нужные поля класса TMyClass в его секцию protected.
2. В другом модуле объявляем:
type
 TMyFriendClass  = class(TMyClass);
3. Ниже этого объявления секция protected будет доступна после приведения типа объекта к TMyFriendClass:
 TMyFriendClass(ObjectOfTMyClass).ProtectedField := ...;


 
Ins ©   (2008-05-07 11:11) [8]


> Юрий Зотов ©   (07.05.08 11:00) [7]

Полагаю, это не совсем то. В данном случае, доступ к закрытим членам получит каждый желающий, а смысл дружественных классов - это разграничение доступа - кому-то доступ разрешен, а кому-то запрещен. Я предлагаю такой вариант:
1. Реализуем в защищаемом классе IInterface и свой интерфейс, содержащий защищаемые методы.
2. Помещаем защищаемые методы в секцию private
3. Реализуем в классе приватное поле (или свойство) AllowAccess: Boolean;
4. Реализуем в классе _QueryInterface таким образом, чтобы он проверял AllowAccess и в случае False не давал доступ к определенному в п.1. интерфейсу.
5. В том же юните реализуем класс-стражник, к которому клиент будет обращаться, когда ему нужен доступ к интерфейсу.
5. У этого класса - один метод: AccessCheck(Client: TObject; Server: TProtectedObject): ISomeInterface. Код этого метода проверяет, имеет ли право Client на доступ (например, ищет класс клиента в неком реестре) и если имеет - выставляет серверу AllowAccess в True, вызывает у него QueryInterface, сбрасывает AllowAccess в False. Если не разрешен - возвращает nil.


 
Ins ©   (2008-05-07 11:24) [9]

Хотя нет, так не получится. Клиент, когда запросит интерфейс у объектной ссылки, а не у интерфейсной, это произойдет напрямую через GetInterface, минуя QueryInterface. Так что либо скармливать клиенту интерфейсную ссылку вместо объектной, либо я еще покурю, и что-либо придумаю :)


 
Игорь Шевченко ©   (2008-05-07 11:38) [10]


> Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных
> friend-классов?


friend-ы в С++ сделаны от безысходности. Аналогов в Delphi нет, потому что они не нужны.


 
Ins ©   (2008-05-07 11:46) [11]

В общем, получилось вот что:
type
 TProtectedObject = class;

 // Интерфейс доступа к защищенному полю X
 IMyInterface = interface
 ["{9963190E-2137-4247-8CDA-EBFAB63D960A}"]
   function GetX: Integer;
 end;

 // Класс, реализующий интерфейс IMyInterface у защищаемого класса TProtectedObject
 TIntfImpl = class(TAggregatedObject, IMyInterface)
 private
   FController: TProtectedObject;
   function GetX: Integer;
 public
   constructor Create(const Controller: TProtectedObject);
 end;

 // Защищаемый класс. Поле X доступно лишь избранным :)
 TProtectedObject = class(TObject, IInterface, IMyInterface)
 private
   X: Integer;
   FAllowAccess: Boolean;
   FIntfImpl: TIntfImpl;
   function GetIntfImpl: IMyInterface;
   // IInterface
   function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
   function _AddRef: Integer; stdcall;
   function _Release: Integer; stdcall;
   // IMyInterface
   // Теперь клиент, запросив интерфейс IMyInterface, не пройдет мимо нашего
   // геттера :)
   property IntfImpl: IMyInterface read GetIntfImpl implements IMyInterface;
 public
   constructor Create;
   destructor Destroy; override;
 end;

 // Класс, у которого мы будем просить доступ
 TSecurityClass = class
 public
   class function QueryAccess(Client: TObject; Server: TProtectedObject): IMyInterface;
 end;

implementation

{ TProtectedObject }

function TProtectedObject._AddRef: Integer;
begin
 Result := -1;
end;

function TProtectedObject._Release: Integer;
begin
 Result := -1;
end;

function TProtectedObject.QueryInterface(const IID: TGUID;
 out Obj): HResult;
begin
 if GetInterface(IID, Obj) then
   Result := 0
 else
   Result := E_NOINTERFACE;
end;

function TProtectedObject.GetIntfImpl: IMyInterface;
begin
 if FAllowAccess then
   Result := FIntfImpl
 else
   Result := nil;
end;

constructor TProtectedObject.Create;
begin
 inherited Create;
 FIntfImpl := TIntfImpl.Create(Self);
end;

destructor TProtectedObject.Destroy;
begin
 FIntfImpl.Free;
 inherited Destroy;
end;

{ TIntfImpl }

constructor TIntfImpl.Create(const Controller: TProtectedObject);
begin
 inherited Create(Controller as IInterface);
 FController := Controller;
end;

function TIntfImpl.GetX: Integer;
begin
 Result := FController.X;
end;

{ TSecurityClass }

class function TSecurityClass.QueryAccess(Client: TObject;
 Server: TProtectedObject): IMyInterface;
begin
 Result := nil;
 if <класс клиента является дружественным> then begin
   Server.FAllowAccess := True;
   try
     Result := Server as IMyInterface;
   finally
     Server.FAllowAccess := False;
   end;
 end;
end;


Теперь получить доступ к полю X можно только запросом у TSecurityClass. А там - мы полностью контролируем, кому давать доступ, а кому - не давать. Список дружественных классов можно реализовать на основе TClassList.


 
Ins ©   (2008-05-07 11:56) [12]

function TProtectedObject.GetIntfImpl: IMyInterface;
begin
if FAllowAccess then
  Result := FIntfImpl
else
  Result := nil; // Тут на самом деле лучше кинуть исключение InvalidTypecast
end;


 
Ins ©   (2008-05-07 12:24) [13]


> friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
> нет, потому что они не нужны.


Не думаю, что от безысходности, они таким образом просто хотели задать более гибкий механизм управления видимостью, чем это делается обычным образом с помощью директив private/protected/public. Гибкий то он действительно более гибкий, но вот то, что сомнительный - это точно.


 
Vladix   (2008-05-07 12:26) [14]


> Ins ©

Код совсем получился неудобочитаемым.. но самое главное - идея, и она понятна. Спасибо за предложенный вариант!


> Игорь Шевченко ©   (07.05.08 11:38) [10]
> friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
> нет, потому что они не нужны.

Ответ, достойный магистра.. Если денег нет, значит они не нужны )))

Что нет в C++ сравнительно с Delphi, чтобы Страуструпу от безысходности пришлось в спешном порядке придумывать "дружественность"? Доступа "всему ко всему" в рамках одного модуля? Если так, то "дружественность" выглядит симпатичнее и по крайней мере объявляется в тексте модулей.


 
Игорь Шевченко ©   (2008-05-07 12:47) [15]

Vladix   (07.05.08 12:26) [14]


> Что нет в C++ сравнительно с Delphi


Модульности


 
Юрий Зотов ©   (2008-05-07 16:50) [16]

> Ins ©   (07.05.08 11:11) [8]

> Полагаю, это не совсем то.

Что и было сказано - "в некотором роде".



Страницы: 1 вся ветка

Текущий архив: 2009.04.19;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.022 c
15-1234906345
Lamer@fools.ua
2009-02-18 00:32
2009.04.19
Сдам мозг в аренду в Киеве


2-1235823045
huge
2009-02-28 15:10
2009.04.19
Сохранение в файл объекта


2-1236084646
StriderMan
2009-03-03 15:50
2009.04.19
Глюк TDateTimePicker.OnChange


2-1236156934
Андрей (начинающий)
2009-03-04 11:55
2009.04.19
Обработка запроса к БД


15-1234787762
Denis Korablev
2009-02-16 15:36
2009.04.19
Темя для диплома АСОиУ