пятница, 27 июня 2008 г.

О наболевшем

Стоит передомной задача... Если следовать утверждению, что клиент не всегда прав, то этот клиент уж сильно не прав. Но он готов платить денег столько, что руководство хватается за него руками и ногами. И что мне делать, если для удовлетворения требований нужно идти в разрез с одним из основных принципов системы? нет ну я могу попробовать систему обмануть, другой вопрос во что оно потом выльется через определенное время... если в клиентской базе полный срач, и названия типа "Аспирин", "Асперин", "Асприн" плодятся в невероятном количестве? уже порядком поднадоело отвечать на письма: "у Вас уже есть этот медикамент в базе, у вас название медикамента с ошибкой написано" брррр...

вторник, 24 июня 2008 г.

Шаблоны проектирования. Часть 2.

Abstract Factory and Factory Method.

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

Реализация Abstract Factory (в наглую сдерта с вики + небольшие правки)
unit AbstractFactory;

interface

type
// AbstractProduct
TCar = class
public
  function Info: string; virtual; abstract;
end;

// ConcreteProductA
TFord = class(TCar)
public
  procedure Test;
  function Info: string; override;
end;

// ConcreteProductB
TToyota  = class(TCar)
public
  function Info: string; override;
end;

// AbstractFactory
TCarFactory = class
public
  function CreateCar: TCar; virtual; abstract;
end;


// ConcreteFactoryA
TFordFactory = Class(TCarFactory)
public
  function CreateCar: TCar; override;
end;

// ConcreteFactoryB
TToyotaFactory = Class(TCarFactory)
public
  function CreateCar: TCar; override;
end;

implementation

{ TFord }
function TFord.Info: string;
begin
  Result:='Ford';
end;

procedure TFord.Test;
begin
  Writeln('TFord.Test');
end;

{ TToyota }
function TToyota.Info: string;
begin
  Result:='Toyota';
end;

{ TFordFactory }
function TFordFactory.CreateCar: TCar;
begin
  Result := TFord.Create;
end;

{ TToyotaFactory }
function TToyotaFactory.CreateCar: TCar;
begin
  Result := TToyota.Create;
end; 

end.

Реализация FactoryMethod
unit FactoryMethod;

interface

type
// "Product"
IProduct = interface
  procedure GetName;
end;

// "ConcreteProductA"
TConcreteProductA = class(TInterfacedObject, IProduct)
  procedure GetName;
end;

// "ConcreteProductB"
TConcreteProductB = class(TInterfacedObject, IProduct)
  procedure GetName;
end;

// "Creator"
ICreator = interface
  function FactoryMethod: IProduct;
end;

// "ConcreteCreatorA"
TConcreteCreatorA = class(TInterfacedObject, ICreator)
  function FactoryMethod: IProduct;
end;

// "ConcreteCreatorB"
TConcreteCreatorB = class(TInterfacedObject, ICreator)
  function FactoryMethod: IProduct;
end;

implementation

{ TConcreteCreatorA }

function TConcreteCreatorA.FactoryMethod: IProduct;
begin
  Result := TConcreteProductA.Create;
end;

{ TConcreteCreatorB }

function TConcreteCreatorB.FactoryMethod: IProduct;
begin
  Result := TConcreteProductB.Create;
end;

{ TConcreteProductA }

procedure TConcreteProductA.GetName;
begin
  Writeln('TConcreteProductA.GetName;');
end;

{ TConcreteProductB }

procedure TConcreteProductB.GetName;
begin
  Writeln('TConcreteProductB.GetName;');
end;

end.

Builder.

Позволяет сконструировать сложный объект на основе “кирпичиков” – более простых объектов. Выходит, что мы можем иметь два объекта одного типа, но с разным содержимым.

Реализация
unit Builder;

interface

uses
SysUtils, Classes;

type

// Product
TProduct = class
private
  FParts: TStringList;
public
  procedure Add(Part: string);
  procedure Show;
  constructor Create;
  destructor Destroy;
end;

// Builder
IBuilder = interface['{52A37564-3B0B-4A5A-ADF0-1DD7AB3A6789}']
  procedure BuildPartA;
  procedure BuildPartB;
  function GetResult: TProduct;
end;

// ConcreteBuilder1
TConcreteBuilder1 = class(TInterfacedObject, IBuilder)
private
  FProduct: TProduct;
public
  procedure BuildPartA;
  procedure BuildPartB;
  function GetResult: TProduct;
  constructor Create;
  destructor Destroy;
end;

// ConcreteBuilder2
TConcreteBuilder2 = class(TInterfacedObject, IBuilder)
private
  FProduct: TProduct;
public
  procedure BuildPartA;
  procedure BuildPartB;
  function GetResult: TProduct;
  constructor Create;
  destructor Destroy;
end;

TDirector = class
public
  procedure Construct(Builder: IBuilder);
end;

implementation

{ Product }

procedure TProduct.Add(Part: string);
begin
  FParts.Add(Part);
end;

constructor TProduct.Create;
begin
  FParts := TStringList.Create;
end;

destructor TProduct.Destroy;
begin
  FreeAndNil(FParts);
end;

procedure TProduct.Show;
var
  I: Integer;
begin
  Writeln('Product Parts -------');
  for I := 0 to FParts.Count - 1 do
  Writeln(FParts[I]);
end;

{ TConcreteBuilder1 }

procedure TConcreteBuilder1.BuildPartA;
begin
  FProduct.Add('PatrA');
  FProduct.Add('PatrB');
  FProduct.Add('PatrC');
end;

procedure TConcreteBuilder1.BuildPartB;
begin
  FProduct.Add('PatrD');
end;

constructor TConcreteBuilder1.Create;
begin
  FProduct := TProduct.Create;
end;

destructor TConcreteBuilder1.Destroy;
begin
  FreeAndNil(FProduct);
end;

function TConcreteBuilder1.GetResult: TProduct;
begin
  Result := FProduct;
end;

{ TConcreteBuilder2 }

procedure TConcreteBuilder2.BuildPartA;
begin
  FProduct.Add('PatrX');
end;

procedure TConcreteBuilder2.BuildPartB;
begin
  FProduct.Add('PatrY');
end;

constructor TConcreteBuilder2.Create;
begin
  FProduct := TProduct.Create;
end;

destructor TConcreteBuilder2.Destroy;
begin
  FreeAndNil(FProduct);
end;

function TConcreteBuilder2.GetResult: TProduct;
begin
  Result := FProduct;
end;

{ TDirector }

procedure TDirector.Construct(Builder: IBuilder);
begin
  Builder.BuildPartA();
  Builder.BuildPartB();
end;

end.

Пример использования
var
  //Builder
  Director: TDirector;
  B1, B2: IBuilder;
  P1, P2: TProduct;
begin
  Director := TDirector.Create;
  B1 := TConcreteBuilder1.Create;
  Director.Construct(B1);
  P1 := B1.GetResult;
  P1.Show;

  B2 := TConcreteBuilder2.Create;
  Director.Construct(B2);
  P2 := B2.GetResult;
  P2.Show;
end;

четверг, 19 июня 2008 г.

Шаблоны проектирования. Глава 1.

Здесь буду высказывать свое мнение и реализацию относительно некоторых паттернов проектирования.
Итак, поехали.

Шаблоны проектирования (паттерн, pattern) — это эффективные способы решения характерных задач проектирования, в частности проектирования компьютерных программ. Паттерн не является законченным образцом проекта, который может быть прямо преобразован в код, скорее это описание или образец для того, как решить задачу, таким образом, чтобы это можно было использовать в различных ситуациях.
http://ru.wikipedia.org/wiki/Шаблоны_проектирования

В принципе вопросов по поводу определения не возникает. Интересно, что подразумевают люди, когда в резюме пишут "Владею шаблонами проектирования"? Имхо как по мне сей факт мало о чем говорит. Ведь можно наизусть зазубрить все 73 шаблона (я столько насчитал) и абсолютно не применять их на практике, либо применять там, где это не нужно (так называемая “избыточная сложность”). А можно и без знания того или иного шаблона своим умом дойти до эффективного решения. Ну да ладно это вопрос таланта, опыта и .т.д. Другой вопрос, что человек, который знаком с сабжем при решении реальной задачи, возможно, вспомнит о том или ином шаблоне и попросту не будет изобретать велосипед (но тут таки опять таки больше вопрос в том уместно ли сей шаблон применять). Так что фразу "Владею шаблонами проектирования" я бы перефразировал в “Знаком с шаблонами проектирования и знаю, в какой ситуации, какой применять”.

Лирическое отступление закончил. Начну пожалуй обзор.

Delegation pattern/Шаблон делегирования
Довольно полезный шаблон позволяющий решить проблему множественного наследования. Суть в том, чтобы передать реализацию того или иного метода связанному объекту.

Реализация 1.
unit DelegationPattern;

interface
uses SysUtils;

type

IDelegateInterface = interface ['{37573FCA-5D5B-4340-9D64-5CB61F672BB6}']
  procedure Method1;
  procedure Method2;
end;

TClassA = class(TInterfacedObject, IDelegateInterface)
public
  procedure Method1;
  procedure Method2;
end;

TClassB = class(TInterfacedObject, IDelegateInterface)
public
  procedure Method1;
  procedure Method2;
end;

TClassDel = class(TInterfacedObject, IDelegateInterface)
private
  FInterface: IDelegateInterface;
public
  procedure Method1;
  procedure Method2;
  procedure ToClassA;
  procedure ToClassB;
end;

implementation

{ TClassA }

procedure TClassA.Method1;
begin
  Writeln('TClassA.Method1');
end;

procedure TClassA.Method2;
begin
  Writeln('TClassA.Method2');
end;

{ TClassB }

procedure TClassB.Method1;
begin
  Writeln('TClassB.Method1');
end;

procedure TClassB.Method2;
begin
  Writeln('TClassB.Method1');
end;

{ TClassDel }

procedure TClassDel.Method1;
begin
  FInterface.Method1;
end;

procedure TClassDel.Method2;
begin
  FInterface.Method2;
end;

procedure TClassDel.ToClassA;
begin
  FInterface := TClassA.Create;
end;

procedure TClassDel.ToClassB;
begin
  FInterface := TClassB.Create;
end;

end.

В принципе в данном примере все прозрачно, с помощью методов TClassDel.ToClassA и TClassDel.ToClassB меняем ссылку на базовый класс, в котором и содержится необходимая реализация.

Реализация 2
type
IImplInterface = interface['{96387A25-C8F4-4275-A553-802F9955CF62}']
  procedure P1;
  procedure P2;
end;

TImplClass = class
  procedure P1;
  procedure P2;
end;

TMyImplClass = class(TInterfacedObject, IImplInterface)
  FMyImplClass : TImplClass;
  property  MyImplClass: TImplClass  read FMyImplClass implements IImplInterface;
  procedure IImplInterface.P1 = MyP1;
  procedure MyP1;
  procedure MyProc;
end;

implementation

{ TImplClass }

procedure TImplClass.P1;
begin
  Writeln('TMyImplClass.P1');
end;

procedure TImplClass.P2;
begin
  Writeln('TMyImplClass.P2');
end;

{ TMyImplClass }

procedure TMyImplClass.MyP1;
begin
  Writeln('TMyClass.MyP1');
end;

procedure TMyImplClass.MyProc;
begin
  Writeln('TMyClass.MyProc');
end;

Также есть класс отвечающий за реализацию. С помощью procedure IImplInterface.P1 = MyP1; можно переопределить метод TImplClass тоесть в моем понимании некий аналог виртуальных методов.

Functional design/Шаблон функционального дизайна

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

Вроде бы это и так понятно. Как говорится нужно отделять “Мух от котлет”. У Мартина в “Быстрая разработка программ: принципы, примеры, практика” на эту тему много чего написано.

Immutable

Идея в использовании неизменяемых объектов. Вполне может быть в некоторых ситуациях полезна. Хотя я не вижу сложности в том, чтобы создать такой объект. Представляю себе это примерно так:

type
TImmutable = class
private
  FData: string;
public
  procedure Write;
  constructor Create(Data: string);
  protected
  property Data: string read FData;
end;

implementation

{ TImmutable }

procedure TImmutable.Write;
begin
  Writeln(FData);
end;

constructor TImmutable.Create(Data: string);
begin
  FData := Data;
end;

вторник, 17 июня 2008 г.

DUnit

Проводить автоматизированное тестирование собственного программного кода хорошо. Первое case средство, которое бросилось в глаза – утилита DUnit (если верить тому, что написано в About ее идея принадлежит самому Кенту Беку (Kent Beck) [падает ниц и бьет челом]). Тулза изначально на sourceforge обитает, и с некоторых пор включена в комплект поставки с Делфи (надо будет ещё нечто подобное для студии посмотреть). Итак, попробую описать основные принципы работы с DUnit.

Для начала создадим новый проект с помощью Test Project Wizard (File -> Other -> Unit Test -> Test Project). Теперь, когда у нас есть готовый проект для тестирования, нам осталось добавить тестируемые модули (File -> Other -> Unit Test -> Unit Case). В результате работы кодогенератора мы получим шаблон теста для модуля, который нужно подключить к проекту (если пользоваться не делфовской DUnit, а той, которая на sourceforge лежит, то для преобразования кода можно использовать утилитку XPGen из Conrlib).

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


Как видно из рисунка, один из моих методов провалился (деление на ноль однако).

procedure TForm3.btnTest1Click(Sender: TObject);
var
  D1, D2: Double;
begin
  D1 := 0;
  D2 := 1/D1;
  ShowMessage(FloatToStr(D2));
end;
А вот второй метод прошел на ура.
procedure TForm3.btnTest2Click(Sender: TObject);
var
  I1: Integer;
begin
  ShowMessage(IntToStr(I1));
end;

Хотя переменная I1 и не определена. Это конечно не ошибка и для обнаружения подобных недоразумений есть подсказки компилятора, чтение и исправление которых в дальнейшем сильно облегчает жизнь.

пятница, 13 июня 2008 г.

Firebird Hymn

Fire, Fire, Fire!!, Fire!
It was old very complex unresolved task
Low budget, too weak chips, and complete time lack
People said with no doubt - it’s impossible to do
But I knew with the fire inside I can do

Power of bytes, embed in my heart
Ready to fight, be enough smart
You need – Firebird!

Build it in right way, break the limits, you can hit them down
Do it simpler, but faster, and faster! We have some fun!
Use Fire triggers, stored procedures, save the time
Burn business rules, use all nice tools
Yeah, yearh, Fire! Fire! Burning inside!

Download it for free
Can I code it again please
Yeah deploy was so fast
It was - Firebird
Firebird, Firebird, Firebird, yeah
Download it for free
Nice to code – try please!

Firebird, Firebird
Firebird, Firebird

Yeah, it’s alright, we’re doin’ right
Yeah, it’s alright, we’re doin’ fine, fine, fine
Firebird,
Firebird, Firebird
Firebird, baby, baby
Firebird, you’ve been Firebird!

среда, 4 июня 2008 г.

О самосовершенствовании

 Последнее время мучают мысли: "Почему учась в институте, так немногому научился...". Думаю причин не так уж и много:

1. Отсутствие серьезного стимула. Чтобы сдать сессию (главный двигатель студента), в далеко не самом престижном ВУЗе страны навыки программирования особо не нужны, так как, много предметов абсолютно не связанных с будущей специальностью, а уровень требований, к знанию предметов профильных, чрезвычайно низок.

2. Отсутствие наблюдения за "зубрами". Большинство адекватных студентов приблизительно равны и ты просто не видишь людей, производительность которых в 10, 100, 1000 раз выше, чем у тебя (программистский синхрофазатрон), поэтому складывается впечатление, что все ок.

3. Привычка конторить... Я думаю каждый хоть раз сдавал абсолютно левую лабу:)

4. Отсутствие "старших товарищей", которые могут в нужном направлении подтолкнуть.

5. Полное отсутствие контроля за качеством кода (на второй моей работе за это взялись серьезно). Я, конечно понимаю, что нужно самому проводить так называемый рефакторинг, но я в те времена даже слова такого не знал:)

6. Уйма свободного времени. Расслабляет, однако.

7. Возможно, нужно было идти работать не на 5-ом курсе, а на годик раньше.

8. Смутное представление о размерах сообщества программистов. В OpenSource почему то даже не пробывал лезть, а необходимость править, а порой даже компилить чужие исходники под линух наводила страх. Были попытки навоять что-либо интересное с одногрупниками, но все они сошли на нет.

Ну вот... думал напишу пару пунктов и успокоюсь, как оказалось самокритиковать себя могу весьма долго.

И так, что мы имеем на сегодняшний день? Работа программистом уже 1,5 года, наконец, пришло понимание (а возможно иллюзия понимания), в какую примерно сторону нужно двигаться, огромное желание развиваться и не тратить время на бесполезные занятия, видны кое-какие примеры для подражания, пусть часть из них и виртуальны. Осознание того, что реальная работа может быть не всегда интересной и порой даже рутинной, но само программирование весьма занятно. Любовь "хорошо покушать", что требует профессионально роста, так как за красивые глазки никто нормально платить не будет.

Так что "вперед и только вперед, не шагу назад".