07.11.2015

Сквозь говнокод. Подопытный код

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

Самым простым и наглядным будет демонстрация эволюции небольшого проекта от процедурного говнокода, до удобного в поддержке ООП проекта.
Итак, задача: есть файл,  котором хранятся сообщения, необходимо реализовать удобный пользовательский интерфейс для просмотра сообщений в этом файле.
Эту задачу я решал в 2007 году. На то время ничего кроме процедурного программирования в Borland C я не знал, поэтому решал так, как мог. Исходный текст я сейчас не трогаю(даже не форматирую), для максимального приближения к задаче поддержки и развития работы с чужим кодом. 


Вся реализация занимает 400 строк вместе с комментариями, поэтому этот пример очень удачен для задачи рефакторинга.

Перед начало рассмотрения кода, я поставлю для себя цели, которые необходимо выполнить:
1. Необходимо расширить список поддерживаемых форматов. Вместо одного самописного формата программа должна уметь работать с cvs, xml, json. При этом должна сохраниться поддержка старого формата.
2. Реализовать Windows интерфейс.
3. Расширить функциональность.
4. Покрыть код автотестами.
Для начала рассмотрим существующий код. Есть структура mail, которая может выступать в качестве элемента двухсвязного списка, так же есть куча указателей на начало, текущий элементы, и так же на элементы начала и конца области видимости. Память для "головы" выделяется всегда.
В Main мешанина из выделения памяти, чтения и парсинга файла, запуска GUI.
К сожалению, у меня нет сейчас Borland C, поэтому хорошо, что сохранился exe-шник, который позволил  посмотреть как это работает. разу же были обнаружены ошибки и лаги в работе. 

//---------------------------------------------------------------------------
struct mail {
 char adresat[20];
 char avtor[20];
 char tema[20];
 char body[256];
 mail *next;
 mail *prev;
};

mail *Head = new mail;
mail *Buf;
mail *Elem;
mail *ViewBegin;
mail *ViewEnd;
mail *Act;
int key;
FILE *datafile;

mail *AddList(mail *EndList);
void mailsplit(char *str);
void shell();
void refrash(mail *Begin, mail *Activ);
void ViewMail(mail *Activ);
void DrawWindow(int p);

//---------------------------------------------------------------------------
//TODO:
int main(int argc, char* argv[])
{
 puts("Start:\n");

 Head->prev = NULL;
 Elem = new mail;
 Head->next = Elem;
 Elem->next = NULL;
 strcpy(Head->body, "head");

 char  buffer[256];
 char ch[2];

 key = 0;
 if ((datafile = fopen(".//dmail//mail.dat", "rt")) == NULL)
 {
  printf("Cannot open input file.\n");
  return 1;
 }
 while (key != 1)
 {
  fscanf(datafile, "%s ", ch);
  mailsplit(ch);
 }

 fclose(datafile);
 clrscr();
 shell();

 //getch();
 return 0;
}
//TODO:
mail *AddList(mail *Elem)
{


 Buf = new mail;

 Buf->next = NULL;
 Buf->prev = Elem;
 Elem->next = Buf;

 return Buf;
};

//TODO:dfgdfg
void mailsplit(char *str)
{
 char i;
 char buf[1024];
 char ChKey;
 char *pt;
 i = str[0];
 ChKey = str[1];
 if (!(i ^ 35))
 {
  if (!(ChKey ^ 49))
  {
   fgets(buf, 20, datafile);
   strcpy(Elem->adresat, buf);
  }
  if (!(ChKey ^ 50))
  {
   fgets(buf, 20, datafile);
   strcpy(Elem->avtor, buf);
  }
  if (!(ChKey ^ 51))
  {
   fgets(buf, 20, datafile);
   strcpy(Elem->tema, buf);
  }

  if (!(ChKey ^ 52))
  {
   fgets(buf, 256, datafile);
   strcpy(Elem->body, buf);
  }
  if (!(ChKey ^ 48))
  {
   Elem = AddList(Elem);
  }
  if (!(ChKey ^ 35))
  {
   key = 1;
  }
 }

}


void shell()
{
 int exitkey;
 char menukey;
 exitkey = 1;
 _setcursortype(_NOCURSOR);
 ViewBegin = Head->next;
 ViewBegin->prev = NULL;
 ViewEnd = ViewBegin;
 Act = ViewBegin;
 for (int i = 0; i < 19; i++)
 {
  ViewEnd = ViewEnd->next;
 }
 DrawWindow(1);
 window(2, 3, 79, 22);
 refrash(ViewBegin, Act);

 while (exitkey)
 {
  //refrash(ViewBegin, Act);
  window(2, 3, 79, 22);
  menukey = getch();

  if (menukey == 27)
   break;

  if (menukey == 80)
  {
   if (Act == ViewEnd)
   {
    ViewEnd = ViewEnd->next;
    ViewBegin = ViewBegin->next;
   }
   if (ViewEnd->next == NULL)
   {
    ViewEnd->next   = Head->next;
   }

   if (Act->next == NULL)
   {
    Act->next = Act;
   }
   else
   {
    Act = Act->next;
   }

   refrash(ViewBegin, Act);
  }

  if (menukey == 72)
  {
   if (Act == ViewBegin)
   {
    ViewEnd = ViewEnd->prev;
    ViewBegin = ViewBegin->prev;
   }
   if (ViewBegin->prev == NULL)
   {
    ViewBegin->prev   = ViewBegin;
   }
   if (Act->prev == NULL)
   {
    Act->prev = Act;
   }
   else
   {
    Act = Act->prev;
   }

   refrash(ViewBegin, Act);
  }
  if (menukey == 13)
  {
   DrawWindow(2);
   ViewMail(Act);
   window(2, 3, 79, 22);
   refrash(ViewBegin, Act);

  }

 }

};

void refrash(mail *Begin, mail *Activ)
{
 clrscr();
 mail *elem;
 elem = new mail;
 elem = Begin;
 int index;
 char *adr, *avt, *tema;
 int i;
 for (i = 1; i < 21; i++)
 {
  textcolor(15);
  textbackground(9);

  if (elem == Activ)
   textcolor(4);
  if (elem != NULL)
  {
   gotoxy(1, i);
   for (index = 0; elem->adresat[index] != '\n' ; index++)
    cprintf("%c", elem->adresat[index]);
   gotoxy(20, i);
   cprintf(" | ");
   for (index = 0; elem->avtor[index] != '\n' ; index++)
    cprintf("%c", elem->avtor[index]);
   gotoxy(40, i);
   cprintf(" | ");
   for (index = 0; elem->tema[index] != '\n' ; index++)
    cprintf("%c", elem->tema[index]);
   puts("\n");
  }

  if (elem == NULL)
   i = 20;
  elem = elem->next;

 }


};
void ViewMail(mail *Activ)
{
 int index;
 int x;
 char menukey;

 do {

  if (menukey == 27)
  {
   break;
  }
  if (menukey == 77)
  {
   if (Activ->next != NULL)
    Activ = Activ->next;

  }
  if (menukey == 75)
  {
   if (Activ->prev != NULL)
    Activ = Activ->prev;
  }
  window(10, 10, 70, 20);
  textbackground(10);
  textcolor(1);
  for (index = 0; index < 10; index++)
   for (x = 0; x < 80; x++)
    cprintf(" ");
  gotoxy(2, 2);
  cprintf("Adresat:");
  for (index = 0; Activ->adresat[index] != '\n' ; index++)
   cprintf("%c", Activ->adresat[index]);
  cprintf("\n\r");
  gotoxy(2, 3);
  cprintf("Avtor  :");
  for (index = 0; Activ->avtor[index] != '\n' ; index++)
   cprintf("%c", Activ->avtor[index]);
  cprintf("\n\r");
  gotoxy(2, 4);
  cprintf("Tema   :");
  for (index = 0; Activ->tema[index] != '\n' ; index++)
   cprintf("%c", Activ->tema[index]);
  cprintf("\n\r");
  gotoxy(2, 5);
  cprintf("Tetx:");
  cprintf("%s \n", Activ->body);
  menukey = getch();
 } while (1);
};

void DrawWindow(int p)
{
 int i;
 char ch;
 if (p == 1)
 {
  window(1, 1, 80, 25);
  textcolor(112);
  textbackground(15);
  ch = 205;
  for (i = 2; i < 80; i++)
  {
   gotoxy(i, 2);
   cprintf("%c", ch);
   gotoxy(i, 23);
   cprintf("%c", ch);
  }
  ch = 186;
  for (i = 2; i < 23; i++)
  {
   gotoxy(1, i);
   cprintf("%c", ch);
   gotoxy(80, i);
   cprintf("%c", ch);
  }

  ch = 188;
  gotoxy(80, 23);
  cprintf("%c", ch);
  ch = 201;
  gotoxy(1, 2);
  cprintf("%c", ch);
  ch = 187;
  gotoxy(80, 2);
  cprintf("%c", ch);
  ch = 200;
  gotoxy(1, 23);
  cprintf("%c", ch);
  gotoxy(1, 25);
  cprintf("Menu:Esc - Exit Enter - Open mail");

 }
 if (p == 2)
 {
  window(9, 9, 71, 23);
  textcolor(0);
  textbackground(15);
  ch = 205;
  for (i = 1; i < 63; i++)
  {
   gotoxy(i, 1);
   cprintf("%c", ch);
   gotoxy(i, 13);
   cprintf("%c", ch);
  }
  ch = 186;
  for (i = 1; i < 14; i++)
  {
   gotoxy(1, i);
   cprintf("%c", ch);
   gotoxy(63, i);
   cprintf("%c", ch);
  }

  ch = 188;
  gotoxy(63, 13);
  cprintf("%c", ch);
  ch = 201;
  gotoxy(1, 1);
  cprintf("%c", ch);
  ch = 187;
  gotoxy(63, 1);
  cprintf("%c", ch);
  ch = 200;
  gotoxy(1, 13);
  cprintf("%c", ch);
  gotoxy(1, 14);
  cprintf(" Menu: Esc - Exit Left/Right - Prev/Next Mail         ");
 }
};



Комментариев нет:

Отправить комментарий