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

Вниз

Использование объектов   Найти похожие ветки 

 
Виктор007   (2007-08-21 18:06) [0]

Решил потренироваться в написании программ используя объекты. Всегда пользовался простыми функциями...

Помогите составить объекты для программы-планировщика.
Задания в планировщике могут быть нескольких типов:
1. Один раз. Параметры: DateTime : TSystemTime (время срабатывания)
2. Ежечасно. Параметры: minute: Int; (минута каждого часа, от 0..59)
3. Ежедневно. Параметры: Время: TSystemTime; (напр 21:49) isПонедельник, isВторник, isСреда и т.п. : Boolean; (по каким дням)
4. Еженедельни и т.п.

Вот собственно я ломаю голову - как построить объектную модель программы (или как это называется).

Подскажите пожалуйста, у меня сложность - слишком много варинтов... я думал сделать обращения к заданиям в таком виде:
Jobs.Items[i].GetNextAlarm... но итемы все получаются разные. В общем даже не знаю как объяснить т.к. но думаю меня поняли. Хочется селать правильно, а не кучей функций и ifов, а используя красивые наглядные объекты...


 
Юрий Зотов ©   (2007-08-21 18:26) [1]

Один из вариантов (неполный, только схематично):

type
 TJobItemType = (jiSingle, jiHourly, jiDaily, jiWeekly, ...);

 TJobItem = class (TCollectionItem)
 private
   FJobItemType: TJobItemType;
   FNextAlarmTime: TDateTime;
 public
   procedure Execute;
     // jiSingle - самоуничтожение, остальные - пересчет FNextAlarmTime.
   property JobItemType: TJobItemType
     read FJobItemType write FJobItemType default jiSingle;
   property NextAlarmTime: TDateTime
     read FNextAlarmTime write FNextAlarmTime;
 end;

 TJobs = class(TCollection)
   ...
 end;

Конкретная реализация зависит от особенностей задачи. Например - кто должен следить за временем, что должно произойти при срабатывании, нужна ли возможность редактирования в design-time и т.п.


 
Виктор007   (2007-08-21 21:36) [2]

Если я правильно понял JobItemType - это тип задания. А меня интересует как реализовать для каждого из типов различные свойства, и чтобы все задания различных
Например для jiSingle: DateTime : TSystemTime
для jiHourly: minute : Integer;
для jiDaily: DateTime  : TSystemTime; Monday, Tuesday, Wednesday... : Boolean;

и т.п. По какому пути лучше пойти в таком случае?


 
Сергей М. ©   (2007-08-22 09:47) [3]


> как реализовать для каждого из типов различные свойства


Это учебная задача ?


 
Виктор007   (2007-08-22 10:22) [4]

Нет, для себя. Лежу уже 2 месяца в больнице, лежать еще 3 месяца - делать нечего. Сейчас пришел немного в себя, привезли ноутбук. Делать катострофически нечего - ничего серьезного делать(думать) нельзя, вот и решил поднатаскаться в основах ООП. Лежу с менингитом. Врач сказал каждый час делать перерывы на 10 минут от компа. Вот и решил написать планировщик, а заодно и освоить ООП.
Задания (напоминания) сделать типа таких: зделать зарядку, на уколы, в столовую, зделать перерыв.

Т.к. сижу в интернету через GPRS, пользоваться поиском и качать книжки довольно дорого :( поэтому обратился на форум.


 
Сергей М. ©   (2007-08-22 10:28) [5]

Ну тогда можно поступить так:

1. Базовый класс TCustomJobItem реализует метод Execute
2. Производные от него классы TSingleJobItem, THourlyJobItem, TDaylyJobItem, TWeeklyJobItem имеют каждый свои собственные свойства требуемых типов.


 
Виктор007   (2007-08-22 18:21) [6]

В общем пока начал делать так:

unit Jobs;

interface
uses Windows;
type
 PSingleJob = ^TSingleJob;
   TSingleJob = packed record
     Time: TSystemTime;
   end;

 PHourlyJob = ^THourlyJob;
   THourlyJob = packed record
     Minute: Integer;
   end;

 TJobItemType = (jiSingle, jiHourly);
 TJobItem = class
 private
   FJobItemType: TJobItemType;
   FCaption: string;
   FEnabled: boolean;
   FNextAlarmTime: TSystemTime;
   FSettings: Pointer;
 public
   constructor Create;
   procedure SetJobItemType(const Value: TJobItemType);
   property JobItemType: TJobItemType
     read FJobItemType;
   property Caption: string
     read FCaption write FCaption;
   property Enabled: boolean
     read FEnabled write FEnabled;
   property NextAlarmTime: TSystemTime
     read FNextAlarmTime write FNextAlarmTime;
   property Settings: Pointer
     read FSettings write FSettings;
 end;

implementation

constructor TJobItem.Create;
begin
FJobItemType := jiSingle;
GetMem(FSettings, SizeOf(TSingleJob));
end;

procedure TJobItem.SetJobItemType(const Value: TJobItemType);
begin
 FreeMem(FSettings);
 FJobItemType := Value;
 case Value of
   jiSingle: GetMem(FSettings, SizeOf(TSingleJob));
   jiHourly: GetMem(FSettings, SizeOf(THourlyJob));
 end;
end;

var i: TJobItem;
initialization
i := TJobItem.Create;
i.Caption := "Сделать зарядку";
i.SetJobItemType( jiSingle );
case i.JobItemType of
 jiSingle: GetLocalTime(PSingleJob(i.Settings).Time);
 jiHourly: PHourlyJob(i.Settings).Minute := 10;
end;
end.


Cкажите, насколько это правильный подход?


 
Юрий Зотов ©   (2007-08-22 23:25) [7]

1. Утечки памяти из-за:
 property Settings: Pointer read FSettings write FSettings;

А вот так их не будет:
 property Settings: Pointer read FSettings write SetSettings;
И в SetSettings надо сначала вызвать FreeMem.

2. Установка Settings "снаружи" не нужна, поэтому так еще лучше:
 property Settings: Pointer read FSettings;
Свойство будет доступно только для чтения. В данном случае так и нужно.

3. Надо перекрыть деструктор и в нем тоже вызвать FreeMem, иначе снова утечки памяти.

4. Нет смысла делать лишние операции (тем более, без нужды фрагментировать память), поэтому:
procedure TJobItem.SetJobItemType(const Value: TJobItemType);
begin
 if FJobItemType <> Value then
 begin
   ...
 end
end;

5. Так будет лучше:
property JobItemType: TJobItemType
 read FJobItemType write SetJobItemType;
И метод SetJobItemType убираем в private.

6. Секция initialization не нужна. Только утечка памяти из-за нее.

7. Пользоваться классом неудобно, потому что требуется приведение типа указателя, да еще и в зависимости от типа задания. Нужно добавить какие-то методы в сам класс, и весь служебный код вынести в них. Идеально, если программист - пользователь класса не должен знать, как этот класс реализован. Он просто вызывает метод класса и получает все, что хочет.


 
Сергей М. ©   (2007-08-23 09:34) [8]


> насколько это правильный подход?


Судя по задействованию рекордов вместо иерархии классов подход вряд ли соответствует желанию "освоить ООП".


 
Юрий Зотов ©   (2007-08-23 09:55) [9]

> Сергей М. ©   (23.08.07 09:34) [8]

Если б не было желания, не было бы этой ветки. А для перестройки мышления нужно время. Придет оно, никуда не денется.

> Виктор007   (22.08.07 18:21) [6]

А по сути Сергей прав - рекорды тут смотрятся весьма криво. Из-за них и п.7 в [7]. Неплохо бы поискать способ от них избавиться.


 
Юрий Зотов ©   (2007-08-23 10:18) [10]

> > Виктор007   (22.08.07 18:21) [6]

А вообще, если хотите освоить ООП, Delphi и VCL, то настоятельно рекомендую капитально проштудировать и понять вот эту книгу:
http://mirknig.com/2006/02/19/sozdanie_originalnykh_komponent_v_srede_Delphi.html

Шедевр. Написана еще в эпоху Delphi 1, но актуальности не потеряла. Тем, кто уже знает Паскаль и уже умеет программировать, ставит понимание объектного подхода буквально за несколько часов. А далее - практика.


 
Виктор007   (2007-08-23 11:33) [11]

Всем большое спасибо за объяснения.
>> Юрий Зотов ©   (23.08.07 10:18) [10]
Эта книга как раз ко мне сучайно попала года 4 назад. До сих пор лежит в шкафу нечитаная, т.к. думал что устаревшая. Теперь обязательно прочитаю - благо в больнице много свободного времеми. Еще раз спасибо, планировщик пока заброшу - повешу таймер на форму :). Если будут остальные вопросы - то уже по ходу чтения книги.


 
Виктор007   (2007-08-23 11:53) [12]

P.S. Вот первый класс который я написал в этом же планировщике.

unit Driver;

interface
uses Windows;
type
 TDriver = class
   private
     FDriverPath: string;
     FDriverName: string;
     fhDriver: THandle;
     procedure SetDriverPath(const fname: string);
     procedure SetDriverName(const Value: string);
     procedure InstallDriver(const fname: string);
     procedure UninstallDriver();
   public
     procedure OpenDriver;
     procedure FreeDriver;
   published
     property hDriver    : THandle read fhDriver;
     property DriverPath : string read FDriverPath write SetDriverPath;
     property DriverName : string read FDriverName write SetDriverName;
 end;
implementation
type
 NTStatus = cardinal;
 PClientID = ^TClientID;
 TClientID = packed record
   UniqueProcess:cardinal;
   UniqueThread:cardinal;
 end;
 PUnicodeString = ^TUnicodeString;
   TUnicodeString = packed record
     Length: Word;
     MaximumLength: Word;
     Buffer: PWideChar;
   end;
 // Прототипы функций
 TRtlInitUnicodeString = procedure(DestinationString: PUnicodeString; SourceString: PWideChar); stdcall;
 TZwLoadDriver         = function(DriverServiceName: PUnicodeString): NTStatus; stdcall;
 TZwUnloadDriver       = function(DriverServiceName: PUnicodeString): NTStatus; stdcall;
var
 RtlInitUnicodeString    :TRtlInitUnicodeString   = nil;
 ZwLoadDriver            :TZwLoadDriver           = nil;
 ZwUnloadDriver          :TZwUnloadDriver         = nil;

// Проверка платформы (true - платформа NT)
function ISNT : boolean;
var
Ver  : TOSVersionInfo;
begin
Result := False;
Ver.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
GetVersionEx(Ver);
case Ver.dwPlatformId of
 VER_PLATFORM_WIN32_NT      : Result := True;   //Windows NT - подходит
 VER_PLATFORM_WIN32_WINDOWS : Result := False;  //Windows 9x-Me - подходит
 VER_PLATFORM_WIN32s        : Result := False;  //Windows 3.x - не подходит
end;
end;

Procedure TDriver.InstallDriver(const fname: string);
var
 Key, Key2: HKEY;
 Pth: PChar;
 dType: dword;
 Image: array [0..MAX_PATH] of Char;
begin
 lstrcpy(Image, "\??\");
 GetFullPathName(PChar(fname), MAX_PATH, PChar(dword(@Image) + 4), Pth);
 dType := 1;
 RegOpenKey(HKEY_LOCAL_MACHINE, "system\CurrentControlSet\Services", Key);
 RegCreateKey(Key, PChar(FDriverName), Key2);
 RegSetValueEx(Key2, "ImagePath", 0, REG_SZ, @Image, lstrlen(Image));
 RegSetValueEx(Key2, "Type", 0, REG_DWORD, @dType, SizeOf(dword));
 RegCloseKey(Key2);
 RegCloseKey(Key);
end;

procedure TDriver.UninstallDriver();
var
Key: HKEY;
begin
RegOpenKey(HKEY_LOCAL_MACHINE, "system\CurrentControlSet\Services", Key);
RegDeleteKey(Key, PChar(FDriverName));
RegCloseKey(Key);
end;

procedure TDriver.SetDriverPath(const fname: string);
begin
 FDriverPath := fname;
end;

procedure TDriver.SetDriverName(const Value: string);
begin
 FDriverName := Value;
end;

procedure TDriver.OpenDriver;
var
 Image: TUnicodeString;
 s: WideString;
begin
 InstallDriver(FDriverPath);
 RtlInitUnicodeString(@Image, PWChar(WideString("\registry\machine\system\CurrentControlSet\Services\"+ fDriverName)));
 ZwLoadDriver(@Image);
 fhDriver := CreateFile(PChar("\\.\"+DriverName), GENERIC_READ + GENERIC_WRITE, 0,
                      nil, OPEN_EXISTING, 0, 0);
end;

Procedure TDriver.FreeDriver();
var
 Image: TUnicodeString;
begin
 if fhDriver = 0 then exit;
 CloseHandle(hDriver);
 RtlInitUnicodeString(@Image, PWChar(WideString("\registry\machine\system\CurrentControlSet\Services\"+ fDriverName)));
 ZwUnloadDriver(@Image);
 UninstallDriver();
 fhDriver := 0;
end;

var
 Lib: THandle;
initialization
 Lib := GetModuleHandle("ntdll.dll");
if Lib<>0 then
  begin
   ZwLoadDriver := GetProcAddress(Lib, "ZwLoadDriver");
   ZwUnloadDriver := GetProcAddress(Lib, "ZwUnloadDriver");
   RtlInitUnicodeString := GetProcAddress(Lib, "RtlInitUnicodeString");
  end;
end.


и собственно сам наследник

unit LKbdDrv;

interface
uses Windows, Driver, KOL;
type
 TKbdDriver = class(TDriver)
 public
   procedure EnableKbd;
   procedure DisableKbd;
   constructor Create;
   constructor Destroy;
 end;
implementation

const
 IOCTL_LOCK_KBD = $0022E014;
 IOCTL_UNLOCK_KBD = $0022E018;

procedure TKbdDriver.EnableKbd;
var
 BytesReturned: dword;
begin
 DeviceIoControl(hDriver, IOCTL_UNLOCK_KBD, nil, 0, nil, 0, BytesReturned, nil);
end;

procedure TKbdDriver.DisableKbd;
var
 BytesReturned: dword;
begin
 DeviceIoControl(hDriver, IOCTL_LOCK_KBD, nil, 0, nil, 0,BytesReturned, nil );
end;

constructor TKbdDriver.Create;
begin
DriverPath := GetStartDir + "lockkbd.sys";
DriverName := "lockkbd";
OpenDriver;
end;

constructor TKbdDriver.Destroy;
begin
FreeDriver;
end;

end.


Единственное - при вызове метода Free экземпляра класса TKbdDriver не вызывается деструктор Destroy


 
Сергей М. ©   (2007-08-23 11:59) [13]


> constructor Destroy;


???

должно быть

destructor Destroy; override;


 
Виктор007   (2007-08-23 12:13) [14]

ммм... точно. Спасибо.


 
Юрий Зотов ©   (2007-08-23 13:02) [15]

1. Класс TDriver отнаследован от TObject, поэтому секция published в нем не имеет смысла. Если сохранение свойств этого класса в DFM не требуется, то используйте public, если требуется - наследуйте его от TPersistent.

2. Методы SetDriverPath и SetDriverName в ТАКОЙ реализации смысла не имеют, то же самое (но проще) дает прямой доступ к полям на запись:
property DriverPath : string read FDriverPath write FDriverPath;
property DriverName : string read FDriverName write FDriverName;


3. Не стал разбираться, проверьте сами: что произойдет, если вызвать метод OpenDriver несколько раз подряд? (Только не говорите, что такого не будет - юзер класса ОБЯЗАТЕЛЬНО это сделает, можно не сомневаться).

4. Про деструктор Сергей уже сказал. И вызов Inherited в его конце обязателен (надо освободить ресурсы, захваченные предками).

5. Переменные ZwLoadDriver, ZwUnloadDriver и RtlInitUnicodeString (и все, что к ним относится) имеют смысл, если все это уже не сделано в VCL. Я не проверял (проверьте сами), но есть подозрение, что в VCL эти функции уже объявлены - а если так, то вся эта возня не нужна, можно просто вызвать функцию. Если же в VCL этого нет, то появляется смысл в том, чтобы вытащить всю низкоуровневую работу в отдельный юнит (типа SysUtils или Windows) и потом использовать этот юнит в других проектах. Такой юнит будет представлять ценность уже и сам по себе.

6. По функционалу - не вникал, проверьте сами вот что.
а). Создаю несколько экземпляров класса TKbdDriver. Что будет?
б). Создаю экземпляр класса TKbdDriver, дизаблю клаву, завершаю программу. Что будет?

7. Главное. Непонятно, зачем в планировщике заданий потребовалось лочить клаву? Складывается ощущение, что написание кода опережает осмысление функционала. То есть, что еще нет четкой продуманности будущей библиотеки классов, что она еще не спроектирована. И если это действительно так, то писать код реализации рано, надо сначала продумать всю будущую библиотеку и написать объявления всех ее классов. Хоть на бумаге, неважно.


 
Виктор007   (2007-08-23 16:03) [16]

Юрий, по всем пунктам понял недочеты. Единственное -
п.5: - модуль предполагается использовать без VCL, т.к. планировщик будет висеть в памяти - то таскать за собой в такой простой программе его не хочется. А динамическое связывание для того чтобы в Win9x не получать непонятной ошибки а просто выводить сообщение о невозможности работы программы в данной среде.
п.6: не пробовал, пишу же класс для себя. Но намек понял, подумаю над этим что-нибудь
п.7: лочить клаву и мышь опять же для себя. Понимаете, если просто выскочит сообщение то я его закрою и про себя подумаю "щас, еще пять минут и пойду выпью таблетки" и в итоге про них забываю, у меня уже пол ящика этих забытых таблеток. Пусть комп просто блокируется на 10 минут чтобы даже Ctrl-Alt-Del не работали - тогда я хочу или не хочу, но придется заняться делами.

> нет четкой продуманности будущей библиотеки классов, что она еще не спроектирована

В этом-то для меня и самая сложность. В институте мы ООП проходили в виде трех страниц определений и все. А сейчас после него хочется со всем разобраться но нет опыта...


 
Юрий Зотов ©   (2007-08-23 16:50) [17]

> Виктор007   (23.08.07 16:03) [16]

> п.5.
Тогда тем более есть смысл вытащить работу с ntdll  в отдельный юнит, чтобы его можно было использовать и без VCL, и вообще без всяких классов.

Динамическое связывание для диагностики - хороший ход, спору нет.

> п.6. и п. 7.
"Для себя" - это обычная ошибка, когда с разработки программ человек переходит на разработку классов. Мышление должно перестроиться, потому что класс (и даже библиотека классов) - это не программа, это кирпичик для множества программ. В этих словах "кирпичик" и "множество" весь фокус и есть. Писать надо не "для себя", а "для кого угодно". Раньше у Вас был один юзер (бухгалтер Лидия Петровна), и Вы думали только о нем. Теперь же у Вас стало два юзера - программист Вася Пупкин и бухгалтер Лидия Петровна. И думать надо уже о них обоих, и писать надо уже для них обоих (причем в первую очередь как раз для Васи).

> В этом-то для меня и самая сложность

Обычная вещь. Читайте Конопку, не пожалеете. Слова класс, экземпляр класса, инкапсуляция, наследование, полиморфизм, поле, свойство, событие, метод, область видимости, статический метод, динамический, метод, виртуальный метод, абстрактный метод, классовый метод, метод диспетчеризации, метод доступа, обработчик сообщения, перекрытие и перегрузка методов, RTTI, метакласс, виртуальный конструктор, сериализация и пр. должны стать такими же родными и понятными, как "папа" и "мама". А линейку наследования TObject - TPersistent - TComponent - TControl - TWinControl/TGraphicControl надо знать чуть ли не наизусть (во всяком случае, понимать, зачем в этой линейке нужен каждый класс, что и как он в этой линейке делает).

Без четкого понимания этих вещей сделать хорошую библиотеку классов довольно проблематично. Так что - читайте Конопку, не пожалеете. Там все это есть (кроме перегрузки методов, но это неважно) и все прекрасно расписано. Еще и перевод очень хороший.


 
@!!ex ©   (2007-08-23 17:46) [18]

Юрий, а не подскажите книжечку по ООП, но не компонентам?
Считал, что знаю прелести ООП достаточно, но почитав эту ветку, понял, что хоть и пишу на ООП, использую лишь малую часть его функионала.


 
Юрий Зотов ©   (2007-08-23 18:21) [19]

> @!!ex ©   (23.08.07 17:46) [18]

По ООП вообще - Гради Буч, видимо.

По ООП конкретно Дельфишному - тот же Конопка. Компоненты - это ведь тоже ООП, да еще какой!


 
Виктор007   (2007-08-30 17:31) [20]

> Юрий Зотов ©   (23.08.07 18:21) [19]
А у вас нет случаем дискетки от книги Рэя Конопки? А то у меня она утерянна :(



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

Форум: "Начинающим";
Текущий архив: 2007.09.23;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.056 c
15-1187797221
Неудачник
2007-08-22 19:40
2007.09.23
Программисты – это армия физиков-неудачников


8-1165841324
SoulFlow
2006-12-11 15:48
2007.09.23
Зрительные образы Winamp


1-1184553942
aclub_it
2007-07-16 06:45
2007.09.23
Delphi &amp; Exel


15-1187879485
ArtemESC
2007-08-23 18:31
2007.09.23
Прогресс


9-1160918345
alpha5
2006-10-15 17:19
2007.09.23
OpenGL не хочет отображать букву "я"





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