Форум: "Прочее";
Текущий архив: 2016.01.24;
Скачать: [xml.tar.bz2];
ВнизРассечение программы на модули. Найти похожие ветки
← →
Pavia © (2015-05-20 20:08) [0]Давно хотел спросить. Как делить программу на модули? Хотелось бы почитать рекомендации.
Библиотеки делятся на стационарные и динамические.
Динамическая библиотека представляет собой отдельный проект. Поэтому в программе они подключаются через заголовочные файлы.
Так вот основной код программы и код стационарных библиотек делятся на модули.
Проблема в том что в Delphi нет namespace, который бы объединил разные файлы в одно цельное пространство. Делить на модули не очень получается. Хотелось бы делить по объектам - один объект один файл. Но как правило так сделать не получается, так как между объектами есть перекрёстные ссылки. И вторая проблема, то что в разных статических библиотеках могут быть объекты с одинаковыми именами. В си это опять таки решается через namespace.
← →
Ega23 © (2015-05-20 20:10) [1]
> Проблема в том что в Delphi нет namespace
Чёй-та?
> И вторая проблема, то что в разных статических библиотеках
> могут быть объекты с одинаковыми именами.var
myObj: UnitName.ClassName;
← →
Pavia © (2015-05-20 20:26) [2]
> Ega23 © (20.05.15 20:10) [1]
Забыл уточнить что речь идет о Delphi 7 и менее.
Разумеется в XE что-то на изобретали. Но это так и не решает проблемы.
function Round(var Goal:TLIntBase2; const a:TRational):Boolean; overload;
var
Moddular:TLIntBase2;
rs:TRelationship;
begin
Result:=true;
MyLongInt2VL.Create(Goal);
MyLongInt2VL.Create(Moddular);
MyLongInt2VL._Div(Goal, a.P, a.Q);
MyLongInt2VL._Mod(Moddular, a.P, a.Q);
MyLong._shl(Moddular,Moddular,1);
MyLongInt2VL.Compare(rs, Moddular, a.Q);
if rs<>rsLess then MyLong.Add(Goal,Goal, MyLong.LIntBase2(1));
Goal.Sign:=a.Sign;
MyLongInt2VL.Destroy(Moddular);
end;
Я не могу написать "With UnitName of", где UnitName это MyLongInt2VL.
А в Си++ так можно.#include <iostream>
int main() {
std::cout << "Hello ";
using namespace std;
cout << "World." << endl; // Тут count идёт без приставки std::
}
Да и нет возможности в Delphi слить(объединить) interface разных файлов.
← →
Pavia © (2015-05-20 20:29) [3]По прошу не уходить в сторону от первого вопроса.
← →
Ega23 © (2015-05-20 20:32) [4]
function Round(var Goal:TLIntBase2; const a:TRational):Boolean; overload;
var
Moddular:TLIntBase2;
rs:TRelationship;
begin
Result:=true;
MyLongInt2VL.Create(Goal);
MyLongInt2VL.Create(Moddular);
MyLongInt2VL._Div(Goal, a.P, a.Q);
MyLongInt2VL._Mod(Moddular, a.P, a.Q);
MyLong._shl(Moddular,Moddular,1);
MyLongInt2VL.Compare(rs, Moddular, a.Q);
if rs<>rsLess then MyLong.Add(Goal,Goal, MyLong.LIntBase2(1));
Goal.Sign:=a.Sign;
MyLongInt2VL.Destroy(Moddular);
end;
Жесть какая.
Не хочу больше рассуждать.
← →
Jeer © (2015-05-20 20:54) [5]Если честно - какой-то бред от неверного понимания концепций структурного и функционального программирования, от слова - совсем.
← →
Pavia © (2015-05-20 21:42) [6]С математическими библиотеками всегда жесть.
> Если честно - какой-то бред от неверного понимания концепций
> структурного и функционального программирования, от слова
> - совсем.
1)
Потому что код выше писался не опираясь на эти концепции. Теория разбилась об скалы практики. Я прекрасно знаю как написать сей код красиво. Данный код мне нужен для совместимости с Delphi 7 в котором нет перегруженных операторов. Поэтому структурированное программирование не подходит. Функциональное программирование мне тоже не подходило, требовался контроль памяти. Не хотелось использовать неявное выделение и освобождение памяти. А это лучше всего сделать так вначале выделить память, а потом уже работать с выделенной памятью. Поэтому от функционального подхода тоже пришлось отказаться.
2) Вы видимо пропустили и не заметили что в изначальном вопросе я упоминал объекты. А это концепция ООП.
В начале создаем объект, а после его используем. Но что-бы снизить число зависимости решено было использовать смесь функций и ООП. Вернее это концепция функционального программирования вынести повторно используемый код и независимый в отдельный модуль. Разделить низкий уровень и высокий уровень. Да непривычно, что низкий уровень сделан через ООП, а высокий напротив через функции. Но мне это нужно по выше изложенным соображениям.
Данный код не отображает всех замыслов. А только показывает одну частную проблему. Я просто выбрал данный как лакмусовую бумажку, которая способна наглядно показать весь масштаб проблемы.
← →
Ega23 © (2015-05-20 21:49) [7]
> А только показывает одну частную проблему.
И ещё раз: это никакая не проблема ни разу.unit Unit1;
implementation
TUnit1NameSpace = class
class function Foo(): Integer; overload;
class function Foo(....): string; overload;
class function Bar(): Integer; overload;
class function Bar(....): string; overload;
end;
unit Unit2;
uses Unit1;
.....
function Round(var Goal:TLIntBase2; const a:TRational):Boolean; overload;
begin
Result:=true;
with TUnit1NameSpace do
begin
Foo;
Bar;
....
end;
end;
Все функции-процедуры делаешь классовыми, собирая их по нужным тебе NameSpace
← →
Игорь Шевченко © (2015-05-20 21:57) [8]
> Как делить программу на модули? Хотелось бы почитать рекомендации
http://royallib.com/book/reymond_erik/iskusstvo_programmirovaniya_dlya_Unix.html
Читать. Кроме рекомендаций по разделению программы на модули в этой книге еще масса полезных рекомендаций. Им тоже желательно следовать.
← →
Rouse_ © (2015-05-20 22:20) [9]Не нужно делить программу, тем более на модули - нужно строить правильную архитектуру.
Остальное само постепенно станет понятным, в том числе - для чего разбивают на модули вне рамок архитектуры проекта.
← →
DVM © (2015-05-20 22:29) [10]
> Pavia © (20.05.15 20:08)
> Проблема в том что в Delphi нет namespace, который бы объединил
> разные файлы в одно цельное пространство. Делить на модули
> не очень получается. Хотелось бы делить по объектам - один
> объект один файл. Но как правило так сделать не получается,
> так как между объектами есть перекрёстные ссылки.
Используйте интерфейсы и проблема исчезнет.
> Ega23 © (20.05.15 20:10) [1]
>
> > Проблема в том что в Delphi нет namespace
>
>
> Чёй-та?
>
Некоторое подобие есть, но реализовано плохо, фактически не дает никаких преимуществ.
← →
DVM © (2015-05-20 22:31) [11]
> Ega23 © (20.05.15 21:49) [7]
> Все функции-процедуры делаешь классовыми, собирая их по
> нужным тебе NameSpace
Ну это не то же самое. Функции не могут при таком подходе находиться в разных модулях, но в одном неймспейсе. Как и классы.
← →
Ega23 © (2015-05-20 22:35) [12]
> Ну это не то же самое. Функции не могут при таком подходе
> находиться в разных модулях, но в одном неймспейсе. Как
> и классы.
Не очень понимаю, зачем это нужно в принципе.
← →
Jeer © (2015-05-20 22:38) [13]Выносите мат. и прочий "мелкий" функционал на уровень функционального программирования.
Пример модульности, понятной по названиям:
tsvBmpRgn.pas 338
tsvColors.pas 222
tsvConsole.pas 196
tsvConst.pas 480
tsvCPU.pas 31
tsvCRC64.pas 87
tsvCrypto.pas 443
tsvCtrl2RTF.pas 390
tsvData.pas 95
tsvDataFile.pas 613
tsvDBIClass.pas 602
tsvDBIConst.pas 16
tsvDBVar.pas 23
tsvDict.pas 226
tsvDirIter.pas 76
tsvDSN.pas 129
tsvDTime.pas 384
tsvExcel.pas 450
tsvExcelUtils.pas 138
tsvFFT.pas 370
tsvFile.pas 1143
tsvFileCompare.pas 141
tsvFileShell.pas 259
tsvFilter.pas 167
tsvFilterBQ.pas 243
tsvFIR.pas 706
tsvFuzzyFind.pas 128
tsvHardware.pas 35
tsvHTMLParser.pas 225
tsvInet.pas 192
tsvIniFiles.pas 53
tsvIntegr.pas 157
tsvIntegral.pas 257
tsvInterpol.pas 44
tsvISAM.pas 1440
tsvISAMConst.pas 23
tsvKeyb.pas 79
tsvLangs.pas 249
tsvLT2UTC.pas 20
tsvMail.pas 188
tsvMath.pas 915
tsvMathConst.pas 59
tsvMathTypes.pas 35
tsvMathUtils.pas 162
tsvMatrix.pas 877
tsvMatrix256.pas 500
tsvMemIniFile.pas 32
tsvMsg.pas 91
tsvNet.pas 654
tsvNumbers.pas 464
tsvOptima.pas 68
tsvParse.pas 48
tsvParseSQL.pas 94
tsvQuery.pas 754
tsvRandom.pas 129
tsvReadTiff.pas 702
tsvReadTiffTags.pas 464
tsvRecoder.pas 379
tsvRegress.pas 113
tsvRegression.pas 456
tsvSAU.pas 23
tsvSignals.pas 342
tsvSignal_.pas 418
tsvSolver.pas 28
tsvSort.pas 314
tsvSort2.pas 326
tsvSortList.pas 236
tsvSound.pas 353
tsvSQL.pas 861
tsvSQLConst.pas 33
tsvStats.pas 521
tsvStrCol.pas 2598
tsvStrCon.pas 2598
tsvStrings.pas 2474
tsvSystem.pas 701
tsvTiming.pas 433
tsvWave.pas 274
tsvWaveUtils.pas 176
tsvWord.pas 214
Twain.pas 2973
← →
DVM © (2015-05-20 22:40) [14]
> Ega23 © (20.05.15 22:35) [12]
Ну без этого обойтись конечно можно.
Классы одного неймспейса видят друг друга даже находясь в разных модулях. Если классов много, то удобно их разносить по разным файлам, а не делать один огромный. Если разносить классы по разным файлам без учета неймспейсов, то в uses кажого модуля надо прописывать слишком много других модулей.
← →
Ega23 © (2015-05-20 22:54) [15]
> DVM © (20.05.15 22:40) [14]
А, вот ты о чём.
Чёрт его знает. С одной стороны - неудобно. С другой - Ты всегда точно знаешь, что что бы ты не использовал в юните, это либо объявлено в этом же юните, либо в одном из тех, что прописаны в uses.
Хрен его знает, лучше это, чем граф из сишных инклудов, где чёрт ногу сломит, или нет.
Ну вот так вот заведено.
← →
Pavia © (2015-05-20 22:56) [16]В данном случае. Всё просто. Но когда делаешь каркас(FrameWork) то приходится делать двунаправленные связи.
Как бы по проще объяснить ...
Есть 3 модуля.
UCompiler, UCompilerError, ULexAnaliser
В каждом есть одноименный объект.
TCompiler, TCompilerError, TLexAnaliser
TCompiler - является каркасом. Каркас нужен для перехвата и подмены разных объектов и их поведения. Он связывает все остальные модули.
Все вызовы идут через него. Поэтому перехват можно сделать легко через Dependency Injection (внедрение зависимостей).
Двунаправленная связь означает что:
- При создание TLexAnaliser в него передаются ссылка на объект типа TCompiler.
- А в самом TCompiler хранится объект типа TLexAnaliser.
Для подмены достаточно породить объект от TLexAnaliser в котором будет перекрыт нужный метод и подсунуть его в TCompiler.
Если бы классы были в одном файле то можно было бы использовать.
TCompiler=class; // Forward (опережающее декларирование)
Но если разделить на два файла,то не получается Forward (опережающее декларирование). Компилятор ругается на отсутствие реализации.
Что-бы проделать такое разделение приходиться использовать TObject. А после приведения типов. Что мне не нравиться.
Поэтому в TLexAnaliser есть кодfunction TParser.NeedSimvol(Simvol: TSimvol): Boolean;
begin
if SimvolsEQ(CurrentSimvol(),Simvol) then
begin
ReadSimvol;
Result:=True;
end else
begin
Result:=False;
Compiler.Error.Add(100,Simvol.Value);
end;
end;function TGramAnaliser.ReadNextSimvol: TSimvol;
begin
repeat
Result:=(Compiler.LexAnaliser as TLexAnaliser).ReadSimvol;
until Not(Result.Kind in [skSpace,skComment]);
end;
А в TCompiler такойprocedure TCompiler.Reset;
var
LexAnaliser:TFileLexAnaliser;
GramAnaliser:TGramAnaliser;
begin
LexAnaliser:=(FLexAnaliser as TFileLexAnaliser);
LexAnaliser.Reset;
GramAnaliser:=FGramAnaliser as TGramAnaliser;
GramAnaliser.Reset;
end;
← →
DVM © (2015-05-20 23:03) [17]
> Pavia © (20.05.15 22:56) [16]
> Что-бы проделать такое разделение приходиться использовать
> TObject. А после приведения типов. Что мне не нравиться.
>
Используйте интерфейсы. Интерфейсы вынесите в отдельные модули.
← →
DVM © (2015-05-20 23:05) [18]
> Pavia ©
Код должен зависеть от абстракций, а не конкретных классов.
← →
Ega23 © (2015-05-20 23:09) [19]
> Он связывает все остальные модули.
Он связывает абстрактные TCustomCompilerError и TCustomLexAnaliser. А про конкретные их реализации он ни сном ни духом.
Ну либо как в [17]
← →
Pavia © (2015-05-20 23:10) [20]
> Используйте интерфейсы и проблема исчезнет.
Честно хотелось бы посмотреть пример. Понятно что интерфейсы можно использовать. Вынести все интерфейсы в отдельный файл. Но по сути они будет целиком дублировать описания класса. Я правильно понял?
← →
DVM © (2015-05-20 23:20) [21]
> Pavia © (20.05.15 23:10) [20]
> Честно хотелось бы посмотреть пример.
Ну вот пример того как разрешить циклические зависимости с помощью интерфейсов и разнести классы по разным файлам.
Делаем интерфейс, кладем его в отдельный модуль uIMyInterface.pas:
IMyInterface1 = interface
...
end;
Делаем второй интерфейс, кладем его в отдельный модуль uIMyInterface2.pas:
IMyInterface2 = interface
...
end;
Делаем 2 класса реализующие интерфейсы:
TMyClass1 = class(IMyInterface1... )
...
private
FMyIntf2: IMyInterface2;
end;
TMyClass2 = class(IMyInterface2... )
...
private
FMyIntf1: IMyInterface1;
end;
Классы TMyClass1 и TMyClass2 могут лежать в разных модулях спокойно теперь, но используют через интерфейсы друг друга.
← →
Ega23 © (2015-05-20 23:20) [22]
> Я правильно понял?
Может быть для начала Гради Буч?
http://www.helloworld.ru/texts/comp/other/oop/ch01.htm
← →
Ega23 © (2015-05-20 23:21) [23]
> но используют через интерфейсы друг друга.
И хрен их просто так теперь убьёшь. :)
← →
Pavia © (2015-05-20 23:24) [24]
> DVM © (20.05.15 23:05) [18]
> > Pavia ©Код должен зависеть от абстракций, а не конкретных
> классов.
>
>
> Ega23 © (20.05.15 23:09) [19]
> > Он связывает все остальные модули.Он связывает абстрактные
> TCustomCompilerError и TCustomLexAnaliser. А про конкретные
> их реализации он ни сном ни духом.Ну либо как в [17]
Это раздувания кода в 2 раза почём зря. В 4 если считать от функционального подхода. Пока будешь писать забудешь о чем думал. По этому от такого подходя решено было отказаться и сразу писать код реализации поумолчанию(defult) минуя абстракцию.
Тем более код не предполагает многочисленных изменения. Поэтому тесная связь между классами меня устраивает.
Совсем забыл написать, как я предполагаю модули они тесно связаны в отличие от библиотек которые самостоятельны. (Просто как-то терминологию ещё не придумал поэтому как бы так)
← →
DVM © (2015-05-20 23:26) [25]
> Pavia © (20.05.15 23:24) [24]
> Это раздувания кода в 2 раза почём зря.
И увеличение гибкости кода даже не в 2, а в N раз пропорционально количеству введенных абстракций.
← →
DVM © (2015-05-20 23:29) [26]
> Pavia © (20.05.15 23:24) [24]
> Поэтому тесная связь между классами меня устраивает.
Так устраивает ли нет? В одном модуле они будут тесно и хорошо связаны друг с другом. Они даже дружественными будут там и даже Private секции будут видеть друг у дружки.
← →
Кто б сомневался © (2015-05-21 00:30) [27]
> Хотелось бы делить по объектам - один объект один файл.
> Но как правило так сделать не получается, так как между
> объектами есть перекрёстные ссылки.
Зачем так мудрить. Классы надо объединять в один юнит логически. Если классы пересекаются и относятся к одному действию (процессы, какое угодно действие) значит их надо делать в одном юните - см. VCL например.
← →
Германн © (2015-05-21 00:48) [28]Удалено модератором
← →
Кто б сомневался © (2015-05-21 00:57) [29]
> Кто б сомневался © (21.05.15 00:30) [27]
Просто когда проект вырастает, и в нем будет куча модулей - программисту будет сложно вспомнить что-где в каком модуле. А если делать один объект - один юнит - это будет сишный ад.
← →
Кто б сомневался © (2015-05-21 01:10) [30]
>
> function TGramAnaliser.ReadNextSimvol: TSimvol;
> begin
> repeat
> Result:=(Compiler.LexAnaliser as TLexAnaliser).ReadSimvol;
>
> until Not(Result.Kind in [skSpace,skComment]);
> end;
Еще можно сделать событие. TGramAnaliser.OnReadNextSimvol - и где то в центральном классе использовать оба класса, который перехватывает событие от TGramAnaliser.
Еще можно при создании TGramAnaliser в конструкторе кидать ссылку на нужный метод из Compiler. И TGramAnaliser будет его вызывать когда нужно.
Таким образом программист будет видеть что конкретно используется в этом классе из класса Compiler.
А вообще что-то не логично в этом коде:
LexAnaliser:=(FLexAnaliser as TFileLexAnaliser);
LexAnaliser.Reset;
GramAnaliser:=FGramAnaliser as TGramAnaliser;
GramAnaliser.Reset;
Эти классы должны быть наследованы - кто-то от кого-то. И вызов Reset у потомка, должен вызывать каскад ресетов у предков. Так проще для понимания кода человеком - меньше путанницы..
Послушай DVM он правильно говорит.
← →
Кто б сомневался © (2015-05-21 01:12) [31]
> Эти классы должны быть наследованы - кто-то от кого-то
Потому что они делают судя по названию похожие задачи, значит возможно нужно сделать базовый абстрактный класс. Например как у TStream.
← →
Кто б сомневался © (2015-05-21 01:26) [32]
> Pavia © (20.05.15 23:24) [24]
>
>
> > DVM © (20.05.15 23:05) [18]
> > > Pavia ©Код должен зависеть от абстракций, а не конкретных
>
> > классов.
> >
>
> Это раздувания кода в 2 раза почём зря.
Абстрактные методы используется в случае если не видно методов у потомка, но при этом нужно как то эти методы заюзать в текущем классе.
Можно еще вместо этого сделать обычный виртуальный метод заглушку - пустой.
TClass1 = class
function DoIt: boolean; virtual; abstract;
procedure SomeProcess;
end
procedure TClass1.SomeProcess;
begin
Result := Doit; // сработает у потомка, а мы здесь получим результат.
end;
← →
Юрий Зотов © (2015-05-21 08:40) [33]> - При создание TLexAnaliser в него передаются ссылка на
> объект типа TCompiler.
> - А в самом TCompiler хранится объект типа TLexAnaliser.
И возникает проблема кольцевой ссылки - так? Если да, то все просто - один uses (или оба) надо перенести в implementation. Такая ссылка кольцевой не считается.
← →
Pavia © (2015-05-21 12:04) [34]
> И возникает проблема кольцевой ссылки - так? Если да, то
> все просто - один uses (или оба) надо перенести в implementation.
> Такая ссылка кольцевой не считается.
Это так и сделано. Вопрос не в кольцевой ссылке, а в кольцевом интерфейсе. Его надо выносить на верхний уровень. Чего как-бы не хотелось делать так как получается, что у нас один интерфейс в 3-х местах дублируется. Но видимо это единственный вариант.
← →
Ega23 © (2015-05-21 12:35) [35]
> Но видимо это единственный вариант.
Единственный вариант - почитать Гради Буча. Там основы.
← →
Pavia © (2015-05-23 15:10) [36]
> Единственный вариант - почитать Гради Буча. Там основы.
Прочитал Буча. Ничего нового. Хотелось бы более практические советы.
← →
Pavia © (2015-05-23 15:51) [37]Развиваю тему заодно хочу услышать критику:
> Библиотеки делятся на стационарные и динамические.
> Динамическая библиотека представляет собой отдельный проект.
> Поэтому в программе они подключаются через заголовочные
> файлы.
>
> Так вот основной код программы и код стационарных библиотек
> делятся на модули.
Префиксы в именах модулей.
U - модуль;
L - статическая библиотека;
I - интерфейсный модуль;
H - заголовочный файл.
Папки.
В папке src - храним исходный код
В подпапках src хранятся библиотеки.
При задействовании библиотеке подключение подключается библиотека L или H.
К примеру:
uses LMyLib;
Файл статической библиотеки L представляет собой объединенную интерфейсную часть модулей U из которых состоит эта библиотека.
Это позволит избавиться и не писать кучу разных uses UUnit1,UUnit2, UUnit3, ..;
Объединение интерфейсной части предполагается при помощи автогенератора. На первых парах ручками.unit LGeometry;
interface
uses UPoints, UPolygons;
{UPoints}
type
PPoint2dR=UPoints.PPoint2dR;
TPoint2dR=UPoints.TPoint2dR;
{UPolygons}
type
FArea= function (Polygon:TPolygon):Real;
var
Area:FArea=UPolygons.Area;
implementation
end.
В Delphi нет удобного механизма для ссылки на функций из другого модуля. Поэтому предполагается для отладки использовать виртуальные функции. А в реализе собирать в файл не только интерфейс (interface) но и реализацию (implementation).
Для решения перекрестных ссылок на разные интерфейсы из разных модулей предполагается создать интерфейсный файл I.
Почему нужен ещё один вид модулей? Дело в том что L нельзя использовать будет ошибка круговой ссылки модулей.
Проблемы такой иерархической структуры известны. Будет незначительная избыточность кода.
К примеру в бибилотеке измерения времени мне нужно производить статическую обработку данных: усреднение, расчет доверительного интервала. Так как эта библиотека может использоваться в разных проектах то статическую обработку мне внести в библиотеку. Хотя у меня есть и библиотека для статической обработки с множеством функций. Но целиком её тащить не целесообразно. Поэтому там где будут использоваться обе этих библиотеке будет дублироваться код этих двух функций(mean, Variance)
← →
Юрий Зотов © (2015-05-23 18:02) [38]> В Delphi нет удобного механизма для ссылки
> на функций из другого модуля.
А это что?
unit Unit1;
interface
function Sum(x, y: extended): extended;
implementation
function Sum(x, y: extended): extended;
begin
result := x + y
end;
end.
// ==========================
unit Unit2;
implementation
uses Unit1;
procedure PrintSum;
begin
WriteLn(Sum(1, 2))
end;
end.
← →
Юрий Зотов © (2015-05-23 18:07) [39]То есть, надо добавить всего одну строку - объявление функции. (в примере это строка 5). После этого ссылайтесь на эту нее из других модулей сколько захотите.
← →
Юрий Зотов © (2015-05-23 18:19) [40]> Pavia ©
А по поводу разбиения на модули - почитайте про восходящее проектирование, в сети материалы на эту тему наверняка есть. VCL с ее кучей модулей по этому принципу и построена - и все нормально, никаких проблем.
Суть в том, что на первом этаже лежат модули, ни от чего не зависящие (например, в VCL это модуль Windows). А на каждом следующем этаже лежат модули, зависящие только от модулей на предыдущих этажах (например, SysUtils, затем Classes и т.д.).
← →
Pavia © (2015-05-23 20:48) [41]
> > В Delphi нет удобного механизма для ссылки> на функций
> из другого модуля.А это что?
Это не то. Так надо будет каждый модуль подключать из библиотеки. Тем более если каждая библиотека состоит из других библиотек, то это превращается в хаус. А хранить всё в одном модуле не хочу.
← →
Pavia © (2015-05-23 20:51) [42]
> Суть в том, что на первом этаже лежат модули, ни от чего
> не зависящие (например, в VCL это модуль Windows). А на
> каждом следующем этаже лежат модули, зависящие только от
> модулей на предыдущих этажах (например, SysUtils, затем
> Classes и т.д.).
Это не суть. Это называется поднять руки и сдаться.
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2016.01.24;
Скачать: [xml.tar.bz2];
Память: 0.61 MB
Время: 0.003 c