Форумное образование
Давно уже меня огорчает одна мысль, что программист-самоучка с помощью форумов может развиться только до определенного уровня, обычно недостаточного для профессиональной работы. То есть, с помощью форумов можно разобраться, как грамотно писать маленькие программы, как использовать какую-то технологию, но знания о грамотной разработке больших программ получить затруднительно.
Ну, возможно, если хорошо разобраться в паттернах, то можно пытаться говорить, о том, какие из них надо применять в той или иной программе. Но так могут быть упущены разные мелочи в коде, которые важны именно в больших проектах.
Кроме того, так не появится опыт работы в команде и т.п.
Примерно то же можно сказать и про обучение по книгам – не всегда поймешь, какую надо читать и для чего. Кроме того, идеи, изложенные в книгах не будут правильно поняты, если нет соответствующей практики.
---
Возможные альтернативы для развития, которые я вижу:
1. Устроиться на работу, где есть команда грамотных программистов, разрабатывающих большие проекты. Это, наверно, наиболее эффективный способ, но не всегда подходящий. Например, если уже есть хорошая работа (по другим параметрам подходящая), или нет подходящей фирмы в родном городе, или там не нужен программист такого уровня.
2. Копаться в исходниках крупных проектов с открытым исходным кодом. Желательно, чтобы эти проекты были бы аналогами того, что создает программист самоучка. Это непредсказуемый путь, так как не отличишь ошибочное решение от правильного, и поспорить об этом не с кем. Да и "переварить" кучу кода бывает тяжело.
3. Участвовать в развитии существующих проектах с открытым кодом. Если есть контакт с другими авторами, то, наверное, это хороший путь для развития. Недостаток тут в том, что проект уже готов, и многие этапы его развития останутся не прочувствованными. Хотя может это и не требуется для большинства программистов.
4. Создать собственный учебный проект с открытым кодом, рассчитанный на команду. Это хороший путь, но наиболее фантастический. Тут и идея интересная нужна, и команда самоучек с похожим опытом, и организатор, и какие-нибудь опытные кураторы... Вроде бы и самоучкам будет опыт, и кураторы будут знать кто на что способен, если надо будет найти программиста для работы. Но вот организовать такое дело обычно невозможно.
---
Вопросы. Заблуждаюсь ли я относительно возможностей форума? Есть ли еще какие-нибудь альтернативы для развития?
Извините, за большой текст.
Пока тут размещу пример прототипа, о котором я говорил
#include <sstream>
#include <string>
#include <list>
using namespace std;
// Компонент представления
class UserInterface
{
public:
void SetString(const string &str){cout << str << endl;}
string GetString(){string str; getline(cin, str, '\n'); return str;}
// выводит набор строк (ФИО адресатов, например)
void SetListString(const list<string> &lstStr)
{
for(list<string>::const_iterator p = lstStr.begin(); p != lstStr.end(); ++p)
{
cout << *p << endl;
}
}
};
// Все! Компонент Представления для консоли готов!
class Controller
{
UserInterface view_;
//AddressBook adrBook_;
void FindCommand(const string &str)
{
view_.SetString("Searching Results: ");
list<string> ls;
for(size_t i = 1; i <= 5; ++i)
{
stringstream oss;
oss << i << ". " << str << "...";
ls.push_back(oss.str());
}
view_.SetListString(ls);
}
void DeleteCommand(const string &str)
{
view_.SetString("Address is Deleted\n");
}
void SetCommand(const string &str)
{
view_.SetString("Address is Added to Base\n");
}
void GetCommand(const string &str)
{
view_.SetString("Name: ***\nEmail: ***@***.***\nTel.: +7 *** ******\n");
}
int step_;
public:
Controller():step_(0){}
void Start(){view_.SetString("Welcome to the Adress Book!\n");}
bool TestView(); // тут считывается и выполняется команда
};
bool Controller::TestView()
{
string command, str;
switch(step_)
{
case 0:
{
view_.SetString("Input the name or input q to Quit");
string str = view_.GetString();
if(str == "q")
return false;
else
{
FindCommand(str);
step_ = 1;
// вариант, когда имя не нашлось пока не анализируем
}
break;
}
case 1:
{
view_.SetString("Select number or input zero to add new Address");
string str = view_.GetString();
if(str == "0")
{
view_.SetString("Function is not available now");
break;
}
// пока только выбор - ввод нового имени не реализован
GetCommand(str);
step_ = 0;
break;
}
default: break;
}
return true;
}
int main()
{
Controller contr;
contr.Start();
while (contr.TestView()){}
return 0;
}
Жмешь f1 и в экране появляется хитрая подмигивающая рожица Phodopus'а, которая предложит тебе информацию о такой большущей куче функций всяких. :D
А если по сабжу, было бы время, я бы может и согласился, а так - не могу щас, увы :)
2Phodopus:
почитай книжку "Мифический человеко-месяц". Смысл инструментальщика - создание подручных интструментов для команды. То есть в проекте, как таковом ты не участвуешь. Например, тебе говорят, сделай такую лабуду, чтобы можно было координировать действия всей команды. Это может быть веб-сервис, где есть возможность конференции (типа жабера или ирки), расшаривания кода, СВН какой-нить и пр. Я к тому, что
инструментальщику в большинстве случаев не надо знать, чем занимается команда. Вот.
инструментальщику в большинстве случаев не надо знать, чем занимается команда. Вот.
Да это я из объяснений Kogrom'a понял :)
Смысл в том, что чтобы быть достойным инструментальщиком - нужно знать чем занимается команда. Иначе гугл - тот же инструментальщик.
нееее, гугл не инструментальщик. гугл дает готовые решения, а инструментальщик делает свои. отличия в том, что в результате получаешь заточенные под конкретные нужды утилиты. и совсем не обязательно знать, что делает команда.
Приведу конкретный пример:
я кажется где-то озвучивал свои сношения с бесконечно падающим инетом. я создал прожку, которая пингует какойнить сайтик, если пинг пропадает, то прога сканирует комп на наличие сетевых подключений и запускает найденное. таким образом у меня перезапускался тырнет. вот те и инструментал. или прога для "замучивания" многоязычности в своих проектов (Localizator), я ее заливал в исходники. то есть эти прожки имеют к основному проекту отношение весьма косвенное.
зы Тут почему лично мне не очень интересно, у меня дофига и больше своих личных проектов, поэтому все свои задумки я тесщу на них. :) Поэтому смысла как такого участвовать не вижу, да и со временем туго.
Вы бы проект бы коммерческим лучше сделали имхо. раз уж команда из нескольких человек, то можно попытаться написать что-нибудь неплохое, за что могут заплатить мани. кроме того, что каждый чему нить научится, так еще и кошель может потяжелее стать. только вам тогда менеджер нужен какой нибудь.
Гугл дает много решений - хороших и плохих. Но готовых, таких, чтоб нажал на кнопку и все заработало - мало. Надо вникать, настраивать и т.д. То есть инструментальщик не обязательно делает что-то новое. Он может заниматься поиском и настройкой.
Надо было ММОРПГ делать... Как я сразу не догадался. И денег бы заработали и известными на весь мир стали :rolleyes: Шучу.
Клиентура - владельцы загородных домов, стремящиеся улучшить комфорт и безопасность своего проживания.
Клиентура - владельцы загородных домов, стремящиеся улучшить комфорт и безопасность своего проживания.
Очередной "Умный дом" ?
Надо было ММОРПГ делать... Как я сразу не догадался. И денег бы заработали и известными на весь мир стали :rolleyes: Шучу.
Напомнило:
http://forum.codenet.ru/showthread.php?p=114150#post114150
:)
А с железками я и сейчас вожусь. Полный стол железок.
У меня есть знакомый инсталлятор. Он жаловался, что не найти софта, удовлетворяющего всем требованиям и работающего с его набором оборудования, приходится заказывать разработку (собственно по этой причине мы с ним и познакомились). В то время он занимался автоматизацией производства, но планировал перейти на дома. Вот и возникла идея разработать систему, объединяющую различные устройства и технологии (видео-наблюдение, система контроля доступа, пожарная и охранная сигнализация, локальная и глобальная сети и т.д.) в гибко-настраиваемом интерфейсе.
У меня есть знакомый инсталлятор. Он жаловался, что не найти софта, удовлетворяющего всем требованиям и работающего с его набором оборудования, приходится заказывать разработку (собственно по этой причине мы с ним и познакомились). В то время он занимался автоматизацией производства, но планировал перейти на дома. Вот и возникла идея разработать систему, объединяющую различные устройства и технологии (видео-наблюдение, система контроля доступа, пожарная и охранная сигнализация, локальная и глобальная сети и т.д.) в гибко-настраиваемом интерфейсе.
Я как-то пробивал эту тему. Так вот оказалось решений МОРЕ и небольшая лужица.
А вот спрос наоборот таки не велик.
Выше я писал, чем мой проект отличается. У меня главное - полученный опыт, а не полученный продукт. Плюс интересное общение с другими программистами. Поэтому, с одной стороны, польза будет почти сразу, а с другой стороны - опыт можно черпать долго.
Но все может быть. Может и я заблуждаюсь.
Но все может быть. Может и я заблуждаюсь.
Да я про ММОРПГ, а не про твой проект. :)
using namespace std;
//*****************************************************
// Интерфейс компонента Менеджера Файла
/*
Файл состоит из ячеек.
Каждая ячейка включает
идентификатор типа int
и набор строк.
*/
class FileManagerMaker
{
public:
// возвращает весь набор n-ных строк
// и набор идентификаторов
virtual pair< vector<int>, vector<string> > GetField(int n) = 0;
// Возвращает содержимое ячейки с заданным идентификатором
virtual vector<string> GetAddress(int id) = 0;
virtual string GetString(int id, int n) = 0;
// Устанавливает значение n-ной строки, с идентификатором id
virtual bool SetString(int id, int n, const string &str) = 0;
// Устанавливает адрес целиком. Возвращает true, если успешно
virtual bool SetAddress(int id, const vector<string> &vs) = 0;
virtual bool DeleteAddress(int id) = 0;
// Возвращает идентификатор или 0 при ошибке
virtual int AddAddress(const vector<string> &vs) = 0;
// Создает объект потомка
static FileManagerMaker* MakeFileManager();
virtual ~FileManagerMaker(){}
};
// Главный класс компонента Менеджера Файла
class FileManager: public FileManagerMaker
{
public:
pair< vector<int>, vector<string> > GetField(int n){}
string GetString(int id, int n){}
vector<string> GetAddress(int id){}
bool SetString(int id, int n, const string &str){}
bool SetAddress(int id, const vector<string> &vs){}
bool DeleteAddress(int id){};
int AddAddress(const vector<string> &vs){};
//...
};
FileManagerMaker* FileManagerMaker::MakeFileManager()
{
return new FileManager;
}
//*****************************************************
// Интерфейс компонента Менеджера Адреса
class AddressManagerMaker
{
public:
virtual vector< pair<string, int> > FindName(const string &str) = 0;
virtual bool SetAddress(int id, const vector<string> &vs) = 0;
virtual bool AddAddress(int id, const vector<string> &vs) = 0;
virtual vector<string> GetAddress(int id) = 0;
virtual bool DeleteAddress(int id) = 0;
static AddressManagerMaker* MakeAddressManager();
virtual ~AddressManagerMaker(){}
};
class AddressManager: public AddressManagerMaker
{
std::auto_ptr<FileManagerMaker> pMnFile_;
public:
vector< pair<string, int> > FindName(const string &str){};
bool SetAddress(int id, const vector<string> &vs){};
bool AddAddress(int id, const vector<string> &vs){};
vector<string> GetAddress(int id){};
bool DeleteAddress(int id){};
AddressManager():pMnFile_(FileManagerMaker::MakeFileManager()){}
};
AddressManagerMaker* AddressManagerMaker::MakeAddressManager()
{
return new AddressManager;
}
//*****************************************************
class Controller
{
std::auto_ptr<AddressManagerMaker> pMnAddress_;
public:
Controller():pMnAddress_(AddressManagerMaker::MakeAddressManager()){}
};
Стал разрабатывать свой формат (как и планировалось по ТЗ, впрочем). При этом старался учитывать 2 условия:
1. Должны быть предусмотрены методы чтения, записи, удаления объекта с идентификатором, без необходимости прочитать (записать) весь файл.
2. Каждый объект базы должен содержать уникальный идентификатор, который не должен меняться в течении всей жизни объекта. После удаления объекта его уникальный идентификатор может быть использован новым объектом.
Для того, чтоб участники проекта (и не участники) могли поправить меня, если иду по неверному пути решил выложить файл с описанием проектирования формата.
Какие есть доводы против XML?
У меня никаких пока.
Но kot_ хотел сделать проект, иллюстрирующий "Использование STL и классов потоков С++ на примере чтения-записи в бинарный файл". А XML, как я знаю - текстовый файл. То есть для того, чтобы поправить какой-то элемент в базе мне придется переписывать весь файл. Что противоречит задумке проекта, если я правильно понимаю. Да и грузить XML-файл лучше целиком, а тут можно частями.
Возможно, стоит предусмотреть импорт/экспорт в этот формат. На следующем этапе разработки.
Эмм... В адресной книге будет полмиллиона записей, что потребует загрузки файла "по-частям"?
Понятия не имею. Скорее всего - нет.
Но дело не в том. Если бы мне нужно было сделать рабочую программу для себя, то я бы даже за XML не брался (лень делать/искать парсер). Использовал бы текстовый файл самого тупого формата и загружал (сохранял) бы его весь одним махом.
Но тут у меня учебный проект - потому, буду считать, что записей у меня будет несколько миллионов. Соответственно и подходы другие.
("Адрес" - основная сущность адресной книги. Состоит из "записей" (ник, е-мейл, телефон и т.д.))
Как-то так пока
#include <list>
#include <vector>
using namespace std;
//************************************************************
// итераторы
//************************************************************
class Iterator
{
public:
virtual ~Iterator() {}
virtual void First() = 0;
virtual void Next() = 0;
virtual bool IsDone() = 0;
virtual string CurrentItem() = 0;
};
//------------------------------------------------------------
class NullIterator: public Iterator
{
protected:
string sData;
public:
NullIterator(string Data)
{
sData = Data;
}
virtual void First() {}
virtual void Next() {}
virtual bool IsDone()
{
return true;
}
virtual string CurrentItem()
{
return sData;
}
};
//------------------------------------------------------------
class ListIterator: public Iterator
{
protected:
list<string> *pList;
list<string>::iterator iter;
public:
ListIterator(list<string> *ptrList)
{
pList = ptrList;
}
virtual void First()
{
iter = pList->begin();
}
virtual void Next()
{
iter++;
}
virtual bool IsDone()
{
return (pList->end() == iter);
}
virtual string CurrentItem()
{
return *iter;
}
};
//************************************************************
// базовый класс для записей адреса (имя, телефон, е-мейл и т.д.)
//************************************************************
class Record
{
public:
virtual ~Record() {}
virtual Iterator* CreateIterator() = 0;
virtual void Insert(string Data) = 0;
virtual void Remove(string Data) = 0;
virtual bool Compare(Record *pRec) = 0;
virtual string ToString() = 0;
};
//------------------------------------------------------------
// пример простой записи адреса
class Nickname: public Record
{
private:
string sData;
public:
Iterator* CreateIterator() {return new NullIterator(sData);}
void Insert(string Data)
{
sData = Data;
}
void Remove(string Data)
{
if (sData == Data) sData.clear();
}
bool Compare(Record *pRec)
{
Nickname *pNick;
if ((pNick = dynamic_cast<Nickname*>(pRec)) == NULL) return false;
return (sData == pNick->sData);
}
string ToString() {return sData;}
};
//------------------------------------------------------------
// пример составной записи адреса
class Telephone: public Record
{
private:
list<string> listTelephones;
public:
Iterator* CreateIterator() {return new ListIterator(&listTelephones);}
void Insert(string Data)
{
listTelephones.push_back(Data);
listTelephones.unique();
}
void Remove(string Data)
{
listTelephones.erase( find(listTelephones.begin(), listTelephones.end(), Data) );
}
bool Compare(Record *pRec)
{
Telephone *pTelephone;
if ((pTelephone = dynamic_cast<Telephone*>(pRec)) == NULL) return false;
return (listTelephones == pTelephone->listTelephones);
}
string ToString()
{
string sBuffer;
for (list<string>::iterator iter = listTelephones.begin();
iter != listTelephones.end(); iter++)
{
if (!sBuffer.empty()) sBuffer += "\r\n";
sBuffer += *iter;
}
return sBuffer;
}
};
//************************************************************
// производящая функция записей
//************************************************************
const unsigned MAX_RECORD_TYPES = 2;
enum RecordTypes{ NICK = 0, TEL };
Record* MakeRecord(RecordTypes Type)
{
switch (Type)
{
case NICK: return new Nickname;
case TEL: return new Telephone;
}
return NULL;
}
//************************************************************
// собственно адрес
//************************************************************
class Address
{
private:
int Id;
vector<Record*> vContent;
void CreateContent()
{
for (unsigned i = 0; i < MAX_RECORD_TYPES; i++) vContent.push_back( MakeRecord(RecordTypes(i)) );
}
void DeleteContent()
{
for (unsigned i = 0; i < MAX_RECORD_TYPES; i++) delete vContent;
vContent.clear();
}
public:
Address()
{
Id = -1;
CreateContent();
}
Address(int Identifier)
{
Id = Identifier;
CreateContent();
}
Address(const Address& Ad)
{
Id = Ad.Id;
CreateContent();
for (unsigned i = 0; i < vContent.size(); i++) *vContent = *Ad.vContent;
}
~Address()
{
DeleteContent();
}
int GetId() {return Id;}
void SetId(int newId) {Id = newId;}
Record* Get(RecordTypes Type)
{
return vContent[Type];
}
Record* operator[] (RecordTypes Type)
{
return vContent[Type];
}
bool Compare(Address& Ad)
{
bool bIdentical = true;
for (unsigned i = 0; i < MAX_RECORD_TYPES; i++)
{
if ( !vContent->Compare(Ad.vContent) )
{
bIdentical = false;
break;
}
}
return bIdentical;
}
vector<string> ToVectorOfString()
{
vector<string> vOut;
for (unsigned i = 0; i < MAX_RECORD_TYPES; i++)
{
vOut.push_back( vContent->ToString() );
}
return vOut;
}
};
//************************************************************
// менеджер файла (заглушка)
//************************************************************
class FileMan
{
typedef vector<string> AddressImage;
vector<AddressImage> DataFile;
public:
FileMan(string sFileName)
{
AddressImage AdImage;
AdImage.push_back("Ivan Ivanov");
AdImage.push_back("111111111");
DataFile.push_back(AdImage);
AdImage.clear();
AdImage.push_back("John Doe");
DataFile.push_back(AdImage);
AdImage.clear();
AdImage.push_back("John Smith");
AdImage.push_back("2222222222");
AdImage.push_back("33333333");
DataFile.push_back(AdImage);
}
~FileMan()
{
DataFile.clear();
}
bool Read(unsigned Identifier, unsigned LineNumber, string& sOut)
{
sOut = "";
if (Identifier >= DataFile.size()) return false;
if (LineNumber >= DataFile[Identifier].size()) return false;
sOut = DataFile[Identifier][LineNumber];
return true;
}
void Write(unsigned Identifier, unsigned LineNumber, string sLine)
{
AddressImage AdImage;
while (Identifier >= DataFile.size()) DataFile.push_back(AdImage);
while (LineNumber >= DataFile[Identifier].size()) DataFile[Identifier].push_back( string("") );
DataFile[Identifier][LineNumber] = sLine;
}
void Delete(unsigned Identifier)
{
if (Identifier >= DataFile.size()) return;
DataFile.erase(DataFile.begin() + Identifier);
}
unsigned GetCountAddresses() {return DataFile.size();}
};
//************************************************************
// менеджер адресов
//************************************************************
class AdMan
{
FileMan *pFile;
public:
static const int ADDRESS_NOT_FOUND = -1;
AdMan(string sFileName)
{
pFile = new FileMan(sFileName);
}
~AdMan()
{
delete pFile;
}
void Add(Address *pAd)
{
vector<string> AdImage = pAd->ToVectorOfString();
for (unsigned i = 0; i < AdImage.size(); i++)
{
pFile->Write(pAd->GetId(), i, AdImage);
}
}
void Delete(int Identifier)
{
pFile->Delete(Identifier);
}
Address* Get(unsigned Identifier)
{
if ( Identifier >= pFile->GetCountAddresses() ) return NULL;
Address *Ad = new Address;
Ad->SetId(Identifier);
string sBuffer;
if ( pFile->Read(Identifier, 0, sBuffer) )
{
Ad->Get(NICK)->Insert(sBuffer);
}
unsigned LineNumber = 1;
while ( pFile->Read(Identifier, LineNumber++, sBuffer) )
{
Ad->Get(TEL)->Insert(sBuffer);
}
return Ad;
}
int Find(Address *pAd)
{
for (unsigned i = 0; i < pFile->GetCountAddresses(); i++)
{
if ( pAd->Compare( *Get(i) ) ) return i;
}
return ADDRESS_NOT_FOUND;
}
unsigned GetCount()
{
return pFile->GetCountAddresses();
}
};
//************************************************************
int main()
{
AdMan am("test");
// выведем все адреса из базы данных
cout << "************* Address Base **************" << endl;
int id = 0;
Address *pCurAd;
while ( (pCurAd = am.Get(id++)) != NULL )
{
vector<string> vOut = pCurAd->ToVectorOfString();
for (unsigned i = 0; i < vOut.size(); i++) cout << vOut << endl;
delete pCurAd;
cout << endl;
}
// добавим новый адрес
Address NewAd;
NewAd.SetId( am.GetCount() );
NewAd[NICK]->Insert("this is added address");
NewAd[TEL]->Insert("+79110001122");
NewAd[TEL]->Insert("01");
am.Add(&NewAd);
// выведем все адреса из базы данных
cout << "********** Address Base: add address **************" << endl;
id = 0;
while ( (pCurAd = am.Get(id++)) != NULL )
{
vector<string> vOut = pCurAd->ToVectorOfString();
for (unsigned i = 0; i < vOut.size(); i++) cout << vOut << endl;
delete pCurAd;
cout << endl;
}
// удалим адрес
am.Delete(1);
// выведем все адреса из базы данных
cout << "********** Address Base: delete address 1 **************" << endl;
id = 0;
while ( (pCurAd = am.Get(id++)) != NULL )
{
vector<string> vOut = pCurAd->ToVectorOfString();
for (unsigned i = 0; i < vOut.size(); i++) cout << vOut << endl;
delete pCurAd;
cout << endl;
}
// найдем адрес
cout << "*********** Find address (John Smith 2222 etc) ***********" << endl;
if (am.Find(am.Get(1)) == am.ADDRESS_NOT_FOUND) cout << "Not found" << endl;
else cout << "Found" << endl;
return 0;
}
Базу придумал самую простую - пока на каждого адресата по 16 полей из 32 символов (потом сделаю настройку полей и символов, если потребуется). Однако весь приведенный ранее интерфейс реализован.
Пока нет никакой надежности, но есть какая-то работоспособность Тестировал на 500000 адресатах. Правда тест провел самый простой.
03.03.2009. - внес изменения и исправление (и, возможно, новые ошибки).
Например, определил
typedef std::pair< std::vector<int>, std::vector<std::string> > PairVIntVString;
исправил ошибку восстановления удаленного адреса.
Добавил одну новую функцию и пару перегруженных.
Пардон, как-то упустил пост.
Вот некоторые ссылки:
housecontrol.ru
intelsystems.ru
intelkey.ru
axico.ru
Если действительно есть интерес и спрос, то можно будет пообщаться.
И еще много всяких мелочей изменил :)
Все вместе выкладываю
Итак, выводы.
Положительные
1. В отличие от других подобных проектов у Учебного Проекта было законченное грамотное ТЗ, хотя и несколько упрощенное.
2. Перед программированием было проведено проектирование. Для согласования структуры программы применялись диаграммы (я использовал UML, остальные участники — что-то самодельное).
3. Проект дожил до кода, до хоть как-то работающих компонент. При этом компоненты делались двумя людьми (возможно, даже тремя). То есть была некая коллективная разработка.
Отрицательные (но тоже полезные):
4. Не было продуманной организации рабочего процесса. Не было идеологии, сроков и т.п. Это вносило хаос и неопределенность в разработку, расслабляло участников.
5. Не было кураторов, если не считать участника форума с ником kot_. Но он был занят чем-то другим и помогал только на начальном этапе.
6. Идея с базой данных была не очень интересна участникам, в особенности, организатору. Меня подкупило то, что идея принадлежит опытному программисту, а значит можно будет попытаться его подключить. Что на начальном этапе и удалось.
7. В проекте не было инструментальщика. Я предполагал, что инструментальщика можно будет выделить из числа участников, или привлечь кого-то со стороны. Этого не получилось. Вероятно, лучше было бы мне самому подготовить все основные инструменты до начала разработки. Или заказать их поиск и настройку.
8. Возможно, не очень удачно была подобрана IDE. В принципе, можно было использовать NetBeans, в котором есть более понятный отладчик, профилировщик, RAD и т.д. Правда, всё это заточено для Java и требует сравнительно мощного компьютера.
Заключение.
Для устранения проблем, приведенных в пунктах 4-8 мне потребуется некоторое время, в течении которого я буду читать литературу и проводить опыты в одиночку. Это потребует приблизительно 2-4 месяца. После этого надеюсь создать новый учебный проект или продолжить этот.
Возможно, я неправильно понимаю о чем речь, но мотивация как раз была - мы хотели научиться лучше программировать/проектировать. И то, что проект в каком-то виде воплотился в работающую программу - тому подтверждение.
Нельзя сказать, что проект неудачен. Для меня проект был разведкой боем. Я накопил немного практического материала, вопросов, которые пригодятся при изучении теории. Дальше можно будет перейти к следующей практической ступени.
Например, сейчас я еще раз разбираюсь, как правильно проектировать программы. Возник вопрос, как надо по умному тестировать их. Придется поискать теорию и в этой области.
Есть область, по которой трудно мне найти теорию - организация рабочего процесса. В принципе, книги есть, но там описываются коммерческие проекты.
У каждого разное понятие мотивации. :)
Выкладываю связку (менеджер файла + менеджер адресов)
На сабвершн их =) надо. Заваливают тут понимаешь форум зипами.
Все-таки лучше разложить по папочкам хотя бы компоненты от разных авторов. Прикладываю ветку(вариант) проекта, где так сделал.
Кроме того, свою часть поправил по замечаниям Нездешнего. По своим замечаниям править пока воздержусь - до ознакомления с теорией по тестированию и рефакторингу.
Странная вещь - компилятор ругается на свои же хедеры. Как исправить - пока не понял.
Проект развивается дальше в замороженном виде...
Пока нет инструментальщика балуемся немного. Однако, я не думаю, что форум от этого сильно страдает.
В качестве альтернативы, можно все скидывать на веб-флешку (была такая тема в общалке). Но пока пользуемся тем, чем проще.
Весна же, оттепель... ;)
Проект или развивается или заморожен :) Развивается в замороженом виде - оригинально однако :)
Скинь в ЛС или как удобно - на чем остановились. По мере сил буду помогать.