Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.51 MB
Время: 0.046 c
2-1235812626
Strate
2009-02-28 12:17
2009.04.19
Интересное поведение компонента TTrayIcon (D2009)


2-1236147878
vat
2009-03-04 09:24
2009.04.19
закрытие программы


15-1235058988
Turok
2009-02-19 18:56
2009.04.19
Компонент для графика функции sin(x)


1-1210091049
Nucer
2008-05-06 20:24
2009.04.19
TThread и OnTerminate


2-1236068306
Евгений Р.
2009-03-03 11:18
2009.04.19
Добавление tToolButton в RunTime





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский