Имя: Пароль:
IT
 
Хранение кода в базе данных - как
0 Гений 1С
 
гуру
02.03.07
18:04
Хочу хранить код (модуль) в базе данных.
Код представляет собой набор функций.

Я передаю в вычислитель ссылку на коллекцию глобальных переменных и запускаю одну из функций модуля, установив ей параметры.

Каким способом добиться максимальной производительности (хочется хранить уже скомпилированный код)?

Код - простейший.
В основном это обращения к методам других объектов, конструирование объектов (список доступных классов опять же в коллекции глобальных переменных), арифметические и строковые операции, ветвления.

То бишь хочется такого: когда я создаю некий объект, я залажу в базу данных (или кэш) и в переменную Code этого объекта заношу уже скомпилированный код этого объекта.

Далее, когда идет обращение к методу например Test() этого объекта, нужно чтобы вызвался метод Test этого объекта.


Или другой, более конкретный пример.

Например я описал классы для базовых элементов - документов, справочников, регистров накопления и т.д.
Допустим я объявляю новый класс справочника номенклатуры Reference_Goods, наследуемый от класса справочники Reference.

Так вот, конкретные вопросы:
1. Как мне сделать, чтобы при выполнении кода я мог сконструировать экземпляр этого класса, т.е. где должно быть размещено хранилище классов.
2. Можно скомпилировать только этот класс (DLL), не перекомпилируя весь код приложения (EXE). То бишь вообще можно компилировать все классы отдельно, а
3. Как мне объявлять в коде обращение к методам класса, если класс неопределен? Использовать метод, который возвращает имя класса и преобразовывать к этому классу или можно как-то прозрачнее реализовать?
   for each el in collection do
       Msg(Convert(el, el.GetType()).GetShtrihCode());
   next
   а хочется просто:
   for each el in collection do
       el.GetShtrihCode();
   next

Есть ли в Pascal /delpy нетипизированный объект как в 1С?
То бишь если x - variant, то как программа при этапе выполнения (а не компиляции) поймет как ей выполнять метод X.PRINT(), даже если этот метод действительно есть у объекта Х?
И сильно ли будет тормозить такой нетипизированный объект.
А как отыграть трюк 1С x["Print"](), т.е. вызов метода по имени?

4. Желательно, чтобы приложение и код пользовательских классов были написаны на одном языке, если я буду использовать Pascal, а разрабатывать приложение в Delpy, смогу ли я достать свободно распространяемый небольшого объема компилятор, чтобы компилировать только пользовательские классы? Основное приложение уже будет скомпилировано.
5. Учитывая, что скорее всего это все будет под KYLYX, те же вопросы и под Linux.

То есть в идеале я хочу иметь набор откомпилированных классов:
Application, Documents, References, Document_Invoice:Documents, Document_Income:Documents, Reference_Goods:References
Нужно их как-то объединить, чтобы можно было создавать любой из этих классов динамически и обращаться к свойствам и методов этих классов.

То бишь работа приложения начнется с создания класса Application.
Метод Application:OnStart может выглядеть так

S=New Reference_Users; //Создаем класс справочника
//Как вариант можно так
//S=References.Create("Users");
//где References - глобальная переменная, хранящая экземпляр класса справочники, аналог Справочники 1С80

R=S.SearchByName(This.CurrentUserName); //Ищем по наименованию
//Если не найден, создаем справочник пользователя
If R=Nothing Then
   SO=S.CreateNew();  
   SO.Name=This.CurrentUserName; //Даем этому справочнику имя пользователя.
   SO.Save();
   S=SO.Link;
EndIF
This.Globals.Add("CurrentUser", S); //Запоминаем в параметрах сеанса текущего пользователя
Желательно также, чтобы классы подгружались только по мере их создания.

Как видно в примере, используется язык без явной типизации (типо Visual Basic).
Конечно если писать на Kylyx(Delphi) придется подумать, как это реализовать.

Если такая хрень с кодом возможна, то тогда разработка приложения будет достаточна проста.
Достаточно объявить класс, сделать его от правильного наследника, откомпилировать - и вуаля!

Тогда форма справочника наследует от базовой формы Form, форма документа тоже от Form.
Форма Form может привязывать к некоторому своему элементу управления Control данные. В Control содержится реквизит Data (аналог данные 1С).
Form справочника умеет передавать данные из объекта справочника этому Control и т.п.
1 Гений 1С
 
гуру
02.03.07
18:28
а мне это важно знать!
2 Mikmak
 
02.03.07
18:39
(1) Обязательно узнаешь, только не нервничай - Гениям нельзя нервничать
3 zalex
 
02.03.07
18:42
Не асилил. Но покури рарус-магазин, у них там скрипты сишные в режиме предприятия пишутся, только как реализовано не вникал, наверняка в ВК реализацию спрятали...
4 Гений 1С
 
гуру
02.03.07
18:45
(3) нет, здесь нужен совет знатока, или тут одни "писатели макросов" собралися, как 1с-ников величают?
5 Garykom
 
гуру
02.03.07
19:01
(0, 1) и особенно (4) сначала слово Delphi( и Kylix) правильно писать научись, потом книжечки умные с www.natahaus.ru почитай, а потом уже стоит такие вопросы задавать ;)
6 Гений 1С
 
гуру
02.03.07
19:09
(5) главное не чистописание и эрудиция а правильный ход мысли
7 АЛьФ
 
02.03.07
19:16
2(6) Правильно! Поэтому сначала научись мыслить, потом научись правильно мыслить, а потом уже п списку из (5).
8 Гений 1С
 
гуру
02.03.07
19:25
(7) в игнор тебя! Просьба по сабжу высказываться
9 АЛьФ
 
02.03.07
19:27
2(8) По сабжу: идиотизм, как всегда.
10 АЛьФ
 
02.03.07
19:28
Ой! Пардон. Я хотел сказать, конечно же: как всегда гениально!
11 Гений 1С
 
гуру
02.03.07
19:28
(9) да ну? батенька, вопрос есть в списке конкретный - есть ли в дельфи объект неопределенного типа (как в Java):

Есть ли в Pascal /delpy нетипизированный объект как в 1С?
То бишь если x - variant, то как программа при этапе выполнения (а не компиляции) поймет как ей выполнять метод X.PRINT(), даже если этот метод действительно есть у объекта Х?
И сильно ли будет тормозить такой нетипизированный объект.
А как отыграть трюк 1С x["Print"](), т.е. вызов метода по имени?

Значит запрос на информацию ты называешь идиотизмом? мды.... стыдитесь...
12 Chieftain
 
03.03.07
11:52
Посмотри по ссылочке - мож поможет тебе
http://www.remobjects.com/page.asp?id={9A30A672-62C8-4131-BA89-EEBBE7E302E6}
13 Ay49Mihas
 
03.03.07
12:10
(11) Твой вопрос вместился бы в одну строку. То,ч то ты вывел на две
страницы --- действительно идиотизм.
14 MMF
 
03.03.07
12:48
(13) просто он прикидывается валенком
15 Гений 1С
 
гуру
05.03.07
18:27
(13) ответ знаешь?
16 Гений 1С
 
гуру
05.03.07
18:28
Если программист пишет в коде:
Y=”Print”;
X[Y]();
То понятно, что на этапе компиляции мы не можем определить, какой метод объекта X хочет вызвать программист, и должны искать соответствие по таблице методов. Единственное, в этой точке кода мы можем сделать хеширование, чтобы проверять, если в очередной раз название требуемого метода соответствует предыдущему, не искать, а выдавать готовый метод (например, в циклах).

Но такая формулировка встречается очень редко.
Обычно программист пишет: X.Print();
Тогда можно завести глобальный двумерный массив, где перечислены все методы (Print, Copy, Paste, Run и т.д….)  для всех классов.

Каждый экземпляр класса (например в Delphy) обычно содержит информацию о своем типе, поэтому можно обратиться к этому двумерному массиву и определить, по какому адресу расположен нужный метод у нужного объекта.

Если метода нет, можно в этой двумерной таблице проставлять ссылку на метод NoMethod соответствующего класса – метод, который обрабатывает случай, когда запрашивают метод, которого нет (чтобы не вводить дополнительную проверку, существует ли сооветствующий метод у класса).

Пример:

class Basic {
function NoMethod() {return false;}
};

class Printable:Basic {
function Print(Msg) { ... ; return true;}
}
class NoPrintable:Basic {
function Calculate() {}
}

function test(X:Basic) {
return X.print("Hello");
//Должно замениться на ClassesMethods[GetType(X)][CONST_PRINT] (“Hello”);
}

a=new Printable; //print есть
b=new NoPrintable; //printа нет
r1=test(a); //вернется true
r2=test(b); //вернется false
Переформулируем вопрос по другому:
Пусть есть некий класс Printable, наследующий от Basic, у него есть некий метод Print(Msg:Basic).
В переменной X хранится экземпляр типа Printable.

function test(X:Basic, T){    (X as X.GetType()).Print(“Hello”);    }

Компилятор какого языка позволяет скомпилировать код так, чтобы на этапе выполнения можно было динамически преобразовать X к другому, заранее неизвестному типу X.GetType()?

Ведь на этапе выполнения я знаю адрес метода Printable::Print, остается только вызвать, так почему я не могу этого сделать?

В принципе, в С++ наверное можно получить явный указатель на любой метод любого класса (или это не так?):

FunctionPointer=X.GetType()::Print;
* FunctionPointer (“Hello”);
Единственное, нужно, чтобы локальные переменные очищала не функция, а точка вызова функции, чтобы не запутаться в количестве локальных переменных.

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

IDispatch не предлагать, вопрос, думается, можно разрешить проще, например написанием препроцессора для кода.
17 Ay49Mihas
 
05.03.07
18:42
(15)
1. Как мне сделать, чтобы при выполнении кода я мог сконструировать экземпляр
этого класса, т.е. где должно быть размещено хранилище классов.
2. Можно скомпилировать только этот класс (DLL), не перекомпилируя весь код
приложения (EXE). То бишь вообще можно компилировать все классы отдельно, а
3. Как мне объявлять в коде обращение к методам класса, если класс неопределен?
Использовать метод, который возвращает имя класса и преобразовывать к этому
классу или можно как-то прозрачнее реализовать?

Это всё очень легко реализуется на PHP. Говорят, и на других языках (Python,
Ruby) --- тоже. Отсюда вывод: ответ я знаю. Сменить язык разработки.

PS: Подозреваю, что в терминах функционального программирования это делается
намного легче :)
18 Гений 1С
 
гуру
05.03.07
18:44
(17) эстественно при этом потерять в скорости. Спасибо, такая мысль мне приходила в голову, не хочу. ;-)
19 Гений 1С
 
гуру
05.03.07
18:44
ведь загвоздочка то маленькая - преобразовать ссылку на базовый класс в ссылку на наследника и вызвать его метод!
20 Ay49Mihas
 
05.03.07
18:46
(18) А в твоём случае --- приобресть геморрой. Но заметь, я лишь сказал, что
знаю. Реализовывать --- твоя задача :)
21 Ay49Mihas
 
05.03.07
18:46
(19) Эта загвоздочка получилась из-за геморройности Delphi.
22 Ay49Mihas
 
05.03.07
18:47
(18) Ну и "Каждый дрочит, как он хочет" (с).
23 Гений 1С
 
гуру
05.03.07
18:55
(21) я думаю не о дельфи. а о с++, если ты о Dispatch, то фигня вопрос, не подходит - тормоза.
24 Ay49Mihas
 
05.03.07
18:59
(23) Гений, ты теорию ООП где-нибудь читал?
25 Ay49Mihas
 
05.03.07
19:01
26 Ay49Mihas
 
05.03.07
19:01
(23) А в вопросах 4 и 5 ясно сказано, что думаешь ты о Дельфи и Кайликс
(мертворожденное поделие).
27 Гений 1С
 
гуру
05.03.07
19:06
(24) давай предметно, а то ты начинаешь надоедать. Читаю и очень хорошо знаю, даже виртуальные таблицы методов, например.
28 Гений 1С
 
гуру
05.03.07
19:06
(26) мне порекомендовали QT.
29 Гений 1С
 
гуру
05.03.07
19:07
(25) PHP - интерпретатор или компилятор?
заметь, если там объект нетипизирован, то каждый раз тратится время на поиск метода по наименованию, что тормоза по определению
30 Ay49Mihas
 
05.03.07
19:08
(28) Начнём с простого. Какая задача? Из-за чего тебе порекомендовали qt? Если
я тебе порекомендую linux-pthreads, я тебе помогу? :)

Продолжим сложным. Зачем производится вызов метода потомка (он называется
виртуальным)?
31 Ay49Mihas
 
05.03.07
19:10
(29) Интерпретатор.
32 Гений 1С
 
гуру
05.03.07
19:11
(30)
QT порекомендовали тут, как кроссплатформенную системную библиотеку GNU:
http://www.sql.ru/forum/actualthread.aspx?tid=402905
(31) интертрепатор - в топпку
33 Гений 1С
 
гуру
05.03.07
19:13
(30) например я перебираю все справочники, т.е.
For each s in Reference.GetAll()
 if s.type=Ref_Goods or s.type=Ref_Clients  then
   s.Print();
 endif
next

метод Print есть у товаров (Goods) и клиентов(Clients)
Короче, когда я не знаю, какой класс на входе, но знаю, что у него есть данный метод
34 Ay49Mihas
 
05.03.07
19:19
(32) Отлично! Ведь Qt никогда не была системной библиотекой GNU :)
Блин, по ссылке тебе написали всё разборчиво (кроме того, что GNU CC --- GNU
Compiler Collection). Чего не так? Qt содержит в себе кроссплатформенные
средства, в том числе и для GUI.
35 Ay49Mihas
 
05.03.07
19:20
Не нужен интерпретатор --- гуляй лесом. Это делается тривиально в любом ОО- и
интерпретируемом языке.
36 Гений 1С
 
гуру
05.03.07
19:42
(34) так я и не говорю, что QT - системная либа...
Кстати, если я напишу 1с на QT, могу я разрабатывать конфы за деньги, или это противоречит QT? ;-) Видимо, конфы должны быть бесплатными. ;-)

(35) вот чем мне нравятся такие заявления - делается элементарно, а как не рассказывается.

Только не надо лечить про:
if s.type=Ref_Goods then
  (s as Ref_Goods).Print();
endif
if s.type=Ref_Clients  then
  (s as Ref_Clients).Print();
endif

Такой вариант убог!
37 quest
 
05.03.07
19:55
http://planet.plt-scheme.org/ исчи, может что найдешь
38 Гений 1С
 
гуру
05.03.07
19:55
(37) не понял, зачем ты меня туда послал?
39 Прохожий
 
05.03.07
19:57
(1) Это все уже поняли. Даже не читая до конца (0)...
40 quest
 
05.03.07
20:00
(38) там море всяких реализаций ООП. Какую хочешь - такую выбери и подправь под себя.
41 Гений 1С
 
гуру
05.03.07
20:10
(40) какой-то хитрый путь вы выбрали
42 Гений 1С
 
гуру
05.03.07
20:16
задал вопрос си плюс плюсникам:
http://www.sql.ru/forum/actualthread.aspx?bid=21&tid=403693
43 Гений 1С
 
гуру
05.03.07
20:17
(40) что значит какую хочешь? Я програмирую например на С++, как я могу выбрать ООП
44 syktyk
 
05.03.07
22:13
Сделай расширения языка 1с: если переменные не типизированы - то используется variant, все замедляется. Проставляются типы- все ускоряется.
45 Ay49Mihas
 
06.03.07
04:20
(36) Спровоцировал.

class BaseClass
{
   public function __call( $name, $args )
   {
       if ( $name == "version" )
           echo "BaseClass v.1.0\n";
       elseif ( $name == "hello" )
           echo "Hello, world!\n";
   }
}

class DerivedClass extends BaseClass
{
   public function __call( $name, $args )
   {
       if ( $name == "version" )
           echo "DerivedClass v.1.0\n";
       elseif ( $name == "hello" )
           echo "Hello, all known world!\n";
   }
}

function some_func( $class )
{
   try
   {
       $class->version();
       $class->hello();
   }
   catch( Exception $e )
   {
       echo "ERROR: ".$e."\n";
   }
}

some_func( $b );
some_func( $d );
46 Hadgehogs
 
06.03.07
06:34
(0) "А по моему вон в них сила. Есть в них что -то дикое, первобытное, все то, что мы - белые давно потеряли... Белые чувствуют это и боятся их..." (Брат 2)

Что вы все на Гения набросились?  Ну пишет он и пишет. Зря завидуете. Именно завидуете. Подспудно и неосознанно. "Как, какой-то Гений, почему он а не я? Да у него ничего не выйдет... У меня бы вышло, если бы сильно постарался, да вот некогда.". И пошли насмехаться. Так легче?

Гений, пиши! Лучше сделать что - то неправильное, чем ничего не делать.. Даже если задумка не выйдет - будет опыт, а это всегда гуд.

И такой - небольшой призыв, вызванный весенним обострением :-)
"Люди, будьте человечнее!!!". Никого не хотел обидеть...
47 Ay49Mihas
 
06.03.07
06:50
(46) Если бы он писал... Ведь он натыкается на давно решённые грабли, которых и
быть-то не должно. Я бы рекомендовал ему написать на интерпретируемом языке
свою мега-задачку, а потом, если уж совсем тормозно, готовые алгоритмы
переписать на компилируемом языке.
А пока он не пишет алгоритмы, он придумывает пути преодоления старых граблей
aka изобретает велосипед. Что здесь почётного, чему завидовать?
48 Гений 1С
 
гуру
06.03.07
18:08
(44) прикол в том, что для нетипизированных тоже не должно замедляться - смотри выше, это возможно!
(45) нет, слишком примитивно.

(47) не фига, я хочу использовать не интерпретатор, а компилятор, может пора под другим углом взглянуть вам на проблему, почитай (0) и докажи, что я не прав.
49 Ненавижу 1С
 
гуру
06.03.07
18:10
(48) зачем компилятор? ты мне хоть объясни
50 Ненавижу 1С
 
гуру
06.03.07
18:12
и потом хранение кода в базе данных уже откомпилированного?
51 Гений 1С
 
гуру
06.03.07
18:17
(49) ну смотри - ядро и прикладные объекты пишутся на одном языке.
Код каждого объекта компилируется и уже хранится в базе данных как скомпилированный.
52 Гений 1С
 
гуру
06.03.07
18:18
(51)+ во первых - скорость, во вторых один язык для ядра и прикладного уровня, значит программист может лучше использовать функции ядра и понимать архитектуру
53 Ненавижу 1С
 
гуру
06.03.07
18:18
так еще и компилятор тебе нужен будет
54 Ненавижу 1С
 
гуру
06.03.07
18:19
И посмотрю я как ты нетипизированные объекты компилировать будешь
55 Ненавижу 1С
 
гуру
06.03.07
18:20
а если платформонезависимо, то только в виртуальной машине, значит только джава, у тебя нет выбора или писать свое, но ты не хочешь, ответ: джава
56 Гений 1С
 
гуру
06.03.07
18:26
(55) я могу сделать прикладные объекты не-объектами и описывать их в DLL... Но компилировать. ;-)
57 Гений 1С
 
гуру
06.03.07
18:26
(53) а что проблема с компилером? GCC...
58 Ненавижу 1С
 
гуру
06.03.07
18:32
(57) да не проблема, просто я ответил на твой вопрос: джава
59 Гений 1С
 
гуру
06.03.07
18:33
(58) платформонезависимо - QT, не нужно джавы. Это библиотека для C++, а компилером C++ может быть GNU GCC, тоже open source!
60 Ay49Mihas
 
06.03.07
18:58
(48) По пунктам --- где примитивно. А в (0) --- поток сознания, не понял почти
ничего (точнее, что понял --- проинтерпретировал выше).
61 Ay49Mihas
 
06.03.07
18:59
(59) Ты забыл про машинные коды :)
62 sapphire
 
06.03.07
19:10
(59) Пиши, пиши на QT :) Лет через 10 напишешь :)
На Java (0) решили еще 10 лет назад.
63 Гений 1С
 
гуру
09.03.07
18:28
(62) мдя, вот после этого и думаешь - где же она, хваленая мощь С++? ;-)
да, джава тоже выглядит симпатичнее, тем более QT все же не абсолютно свободна, хотя это и не принципиально
64 BabySG
 
01.07.07
22:25
(69) "Вызвать метод наследника, если известно что этот метод точно есть"
Могу показать свою наработку реализации callback'ов - не важно знать заранее - есть метод или нет:
.   typedef std::list< std::pair< std::pair< int, CObj*> , void* > > WCList;
.   template < class TObj >
.   bool addCallback( int cmd, CObj* parent, void (TObj::*method)() ){            
.      union{
.         void *ptr;
.         void (TObj::*methodPtr)();
.      };
.      std::pair< int, CObj* > objCmd( cmd, parent );
.      methodPtr = method;
.      std::pair< std::pair<int, CObj*>, void* > cmdCallFunc( objCmd, ptr );    // Создаем пару команда+указатель
.      wc.push_back( cmdCallFunc );                                            // и запихиваем в список
.      return true;
.   };    

Что делает addCalback: есть у класса определенные события (к примеру) которые хранятся в перечислении cmd - необходимо при наступлении какого-либо события вызвать произвольный метод в произвольном классе.
Достигается путем передачи значения команды, указателя БАЗОВОГО класса вызываемого объекта и указателя на функцию-член нужного нам класса.
(хак не детский - но по другом не придумал как :) )

А вот такой ф-ей, собственно, и вызываем наш метод:

.   void CWinObj::callback( int cmd ){
.
.   if( !wc.empty() ){                            // Если у нас есть обработчики...
.   WCList::iterator i = wc.begin();                    // создаем наш итератор
.   while( i != wc.end() ){                        // ползаем по списку, пока не найдем нашу пару...
.      if( (*i).first.first == cmd ){                    // *Если это наша команда...
.         union{                                // создаем структурку для вытаскивания указателя на метод
.            void *ptr;
.            void (CObj::*methodPtr)();                        // CObj - так как насрать на this-указатель
.         };
.         ptr = (*i).second;
.         (((CObj*)((*i).first.second ))->*methodPtr)();                // Ну и вызываем его
.      break;
.      }else                                // *не нашли пару
.         i++;                                // смотрим следующее значение списка
.      };
.   }
 
Вот эта строка (((CObj*)((*i).first.second ))->*methodPtr)(); и есть вызов неопределенного метода неопределенного класса.
Единственное ограничение на работу этой функции в данный момент (руки никак не доходили) пока нельзя передавать неопределенные аргументы - все лень дописать - хотя это довольно просто.

ЗЫ. А если учесть, что задача стояла вызвать просто метод наследника - дык это вообще просто... Передаешь указатель на метод в базовый и по базовому указателю его вызываешь -= элементарное действие. (но в борланде работать не будет скорее всего)
65 BabySG
 
01.07.07
22:26
+(64) - ошибочка - не (69), а (63) :)
66 Чес
 
02.07.07
03:16
(33) За такое "программирование" надо бить. Тупо ногами.
67 rassigor
 
02.07.07
03:22
А кем вообще работает гений?Креативным разработчиком?Эт скока же времени надо на разработку идей?Он наверно своей жене все мозги высущил:):)
68 Чес
 
02.07.07
03:24
Он мобилами торгует
69 rassigor
 
02.07.07
03:27
(68)хм..
70 quest
 
02.07.07
03:31
(68) а разве не раздает?
71 Череп
 
02.07.07
06:44
(36) Конфы могут быть платными, но ты должен либо купить у Qt лицензию, либо предоставлять заказчикам исходный код по первому требованию.
72 dbert
 
02.07.07
07:14
AFAIK, в C# (.NET) можно компилировать "на лету"
73 Кецалькоатль
 
02.07.07
15:39
(72) Да, и выполнять.
Есть еще сериализация объектов (в базе чтоб хранить)
74 sapphire
 
02.07.07
15:41
(0) Ты вообще ничего не знаешь, или так придуриваешься?
75 sapphire
 
02.07.07
15:41
(0) гугли classloaderы
76 sapphire
 
02.07.07
15:41
можещь еще про паттерны(шаблоны) программирования почитать.
77 Кецалькоатль
 
03.07.07
09:44
Лучше не VS 2005, a VS ORCAS (2008), там прикольные вещи есть, типа ADO.NET Entity, LINQ to Objects, LINQ to SQL, лямбда выражения.

http://blogs.msdn.com/wesdyer/default.aspx
78 Кецалькоатль
 
03.07.07
09:46
И самое главное WPF, генерация форм описанием - XAML. Интерфейс и его поведение становится данными, удобно хранить в базе и модифицировать.
79 BabySG
 
03.07.07
20:59
(75) Java? На такой проект?
Глупец, лишенный способности посмеяться над собой вместе с другими, не сможет долго выносить программирование. Фредерик Брукс-младший