Implementing Factory Method with Pascal

classic Classic list List threaded Threaded
25 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Implementing Factory Method with Pascal

Luciano de Souza
Hello all,

I'd like to understand how to implement the Factory Method pattern in Pascal.

In my example, there are four classes:
1. TAnimal - The parent and abstract class;
2. TDog and TCat - The specialized classes;
3. TAnimalFactory - The class which creates instances of animals,
having received a enumerator as a parameter.

The method "live" is common to TAnimal, Tdog and TCat. However, "bark"
is found only in TDog.

In the following code, the compiler says that animal does not
possesses "bark" as a method. It's right. The behaviour is implemented
only in the derived class.

But, if this not works, how to implement the Factory Method in Freepascal?

                program test;
{$mode objfpc}{$H+}

uses
tsbase;

var
animal: TAnimal;
BEGIN
animal := TAnimalFactory.create(atDog);
try
animal.bark; // Here is the error. If the call were "animal.live, no
errors would be shown. The code does not fails with (animal as
TDog).bark, but it does not make sense. The factory should allow to
create the instance only with the enumerator, without casting.

finally
animal.free;
end;
END.
               
                unit tsbase;
{$mode objfpc}{$H+}

interface
type
TAnimal = class(TObject)
public
procedure live; virtual; abstract;
end;

TDog = class(TAnimal)
public
procedure live; override;
procedure bark;
end;

TCat = class(TAnimal)
public
procedure live; override;
end;

TAnimalType = (atDog, atCat);

TAnimalFactory = class(TObject)
public
class function create(AType: TAnimalType):TAnimal;
end;

implementation
{TDog}
procedure TDog.live;
begin
writeln('Um cachorro vive');
end;

procedure TDog.bark;
begin
writeln('Um cachorro foge');
end;

{TCat}
procedure TCat.live;
begin
writeln('Um gato vive');
end;

{TAnimalFactory}
class function TAnimalFactory.create(AType: TAnimalType):TAnimal;
begin
case AType of
atDog: result := TDog.create;
atCat: result := TCat.create;
end;
end;

end.

Well, I am studying design patterns, but I really does not understand
how to solve this problem.

I thank you for any tip.

Best regards,

--
Luciano de Souza
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Mattias Gaertner

One solution:

var
  animal: TDog;
BEGIN
  animal := TAnimalFactory.create(atDog) as TDog;
  try
    animal.bark;
  finally
    animal.free;
  end;
END.


Mattias
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Luciano de Souza
Yes, it works. But in this case, I don't understand the sense of the pattern.

If I need to do "TAnimalFactory.create(atDog) as Tdog", perhaps, it
would be better not to use a factory, doing simply "TDog.create".

I don't understand if I have implemented the pattern wrongly or if
this pattern, becose of the strong typing of the language, does not
make sense in Pascal.

2015-11-27 19:30 GMT-02:00, Mattias Gaertner <[hidden email]>:

>
> One solution:
>
> var
>   animal: TDog;
> BEGIN
>   animal := TAnimalFactory.create(atDog) as TDog;
>   try
>     animal.bark;
>   finally
>     animal.free;
>   end;
> END.
>
>
> Mattias
> _______________________________________________
> fpc-pascal maillist  -  [hidden email]
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>


--
Luciano de Souza
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Graeme Geldenhuys-6
In reply to this post by Luciano de Souza
On 2015-11-27 21:21, luciano de souza wrote:
> I'd like to understand how to implement the Factory Method pattern in Pascal.

See my "Simple Factory Pattern" article. I've written about many other
design patterns too.

  http://geldenhuys.co.uk/articles/


Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marcos Douglas B. Santos
In reply to this post by Luciano de Souza
On Fri, Nov 27, 2015 at 7:44 PM, luciano de souza <[hidden email]> wrote:
> If I need to do "TAnimalFactory.create(atDog) as Tdog", perhaps, it
> would be better not to use a factory, doing simply "TDog.create".

You're right, use simply TDog.Create.
Why do you have a factory to create different types of animals
(classes)? Doesn't make sense. If you need a factory, you should use a
factory for dogs, another for cats and so on.
Another tip: Factories resolve some problems but there is a cost. The
factory will creates your instance (object) but it know only one
constructor, ie, the base class constructor.
Objects should be immutable at first place, only if you have a good
reason for don't use immutability. So, if they should be immutable,
you have only the constructor to instantiate your object, passing
arguments for it. If you have a constructor without arguments (using a
base class for example), you won't pass arguments to instantiate your
classe properly.
Another tip: use interfaces, not inheritance. Inheritance is evil. You
always will have problems using inheritance. Instead, use small
objects with few methods (2-5) to resolve just one problem. You don't
need inheritance for that. Your code will be more simpler and
customizable.
Decorator Pattern is more powerful than inheritance. Read about it.

Best regards,

Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Luciano de Souza
Marcos,

Your answer and the excelent article of Graeme clarify the question.

If I want to change the type of the instance, better is to use
generics. A factory is only a batch builder of the same objects.

Graeme, your article opened my mind. The mappings and the registers
caused me a very strong impression. Thank you!
2015-11-27 21:48 GMT-02:00, Marcos Douglas <[hidden email]>:

> On Fri, Nov 27, 2015 at 7:44 PM, luciano de souza <[hidden email]>
> wrote:
>> If I need to do "TAnimalFactory.create(atDog) as Tdog", perhaps, it
>> would be better not to use a factory, doing simply "TDog.create".
>
> You're right, use simply TDog.Create.
> Why do you have a factory to create different types of animals
> (classes)? Doesn't make sense. If you need a factory, you should use a
> factory for dogs, another for cats and so on.
> Another tip: Factories resolve some problems but there is a cost. The
> factory will creates your instance (object) but it know only one
> constructor, ie, the base class constructor.
> Objects should be immutable at first place, only if you have a good
> reason for don't use immutability. So, if they should be immutable,
> you have only the constructor to instantiate your object, passing
> arguments for it. If you have a constructor without arguments (using a
> base class for example), you won't pass arguments to instantiate your
> classe properly.
> Another tip: use interfaces, not inheritance. Inheritance is evil. You
> always will have problems using inheritance. Instead, use small
> objects with few methods (2-5) to resolve just one problem. You don't
> need inheritance for that. Your code will be more simpler and
> customizable.
> Decorator Pattern is more powerful than inheritance. Read about it.
>
> Best regards,
>
> Marcos Douglas
> _______________________________________________
> fpc-pascal maillist  -  [hidden email]
> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>


--
Luciano de Souza
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Anthony Walter-3
In reply to this post by Marcos Douglas B. Santos
type
  IBarkable = interface(IInterface)
  ['{B241068F-2ED9-43C7-066B-778B94CB58F9}']
    procedure Bark;
  end;

  TAnimal = class(IInterface)
  end;

  TDog = class(TAnimal, IBarkable)
  public
    procedure Live; override;
    procedure Bark;
  end;

and later ...

if Animal is IBarkable then (Animal as IBarkable).Bark; 


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marcos Douglas B. Santos
In reply to this post by Luciano de Souza
On Fri, Nov 27, 2015 at 10:15 PM, luciano de souza <[hidden email]> wrote:
> If I want to change the type of the instance, better is to use
> generics. A factory is only a batch builder of the same objects.

OK, but you don't need generics either ;)

Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marcos Douglas B. Santos
In reply to this post by Anthony Walter-3
On Fri, Nov 27, 2015 at 10:16 PM, Anthony Walter <[hidden email]> wrote:
> if Animal is IBarkable then (Animal as IBarkable).Bark;

Your approach is much better, but don't use casting is even better
when we working with a true object oriented, because casting is a
"procedural command" for the compiler.


Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Graeme Geldenhuys-6
In reply to this post by Anthony Walter-3
Hi Anthony,

On 2015-11-28 00:16, Anthony Walter wrote:
> type
>   IBarkable = interface(IInterface)
>   ['{B241068F-2ED9-43C7-066B-778B94CB58F9}']
>     procedure Bark;
>   end;
> ...snip...

That is a much better solution for what Luciano wants to accomplish.


> and later ...
>
> if Animal is IBarkable then (Animal as IBarkable).Bark;

This is not good usage of Interfaces. Use the interface variable instead.

var
  intf: IBarkable
begin
  if Supports(Animal, IBarbable, intf) then
  begin
    intf.Bark;
    // you can continue here using intf further without casting
  end;



Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Graeme Geldenhuys-6
In reply to this post by Marcos Douglas B. Santos
Hi Marcos,

On 2015-11-27 23:48, Marcos Douglas wrote:
> Why do you have a factory to create different types of animals
> (classes)? Doesn't make sense.

Maybe in Luciano's simple example a factory is not the best use case,
but the Simple Factory design pattern can be very useful in other cases.

eg: tiOPF uses the Simple Factory extensively in many areas. eg:
encryption. tiOPF defines a base class with abstract methods for using
encryption. We then have various descendant classes that implement
different encryption. Each of the descendants register themselves with
the factory (tiOPF likes to use the uses clause and Initialization
section for this). In your own code you then request an encryption type
and get a instance object back. You use that instance via the API
(interface) defined by the abstract methods.


> Objects should be immutable at first place, only if you have a good
> reason for don't use immutability. So, if they should be immutable,

This has nothing to do with Luciano's problem.


> Another tip: use interfaces, not inheritance. Inheritance is evil.

Indeed, Interfaces might be a more elegant solution to Luciano's problem
- for what he wants to achieve. Inheritance is NOT evil. It's not that
black and white. Both Interfaces and Inheritance have their pros and
cons - you just need to study your problem and then decide which is the
better fit.

@Luciano,
What you described is the Simple Factory design pattern. I suggest you
also study the Factory Method design pattern, to see the difference and
where you can use them.

Two [I have many more suggestions too] Design Pattern books I highly
recommend are:

  * Design Patterns - Elements Of Reusable Object Oriented Software.
    Also known as the GoF (Gang of Four) book.

  * Head First - Design Patterns
    This one has a very different writing style, but I found this fun
    take on design patterns works very well.


Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marcos Douglas B. Santos
On Sat, Nov 28, 2015 at 7:32 AM, Graeme Geldenhuys
<[hidden email]> wrote:
>
> Maybe in Luciano's simple example a factory is not the best use case,
> but the Simple Factory design pattern can be very useful in other cases.

I agree in both affirmations. But as I said before, Factories resolve
some problems but there is a cost.

Thank for explanation about tiOPF.

>> Objects should be immutable at first place, only if you have a good
>> reason for don't use immutability. So, if they should be immutable,
>
> This has nothing to do with Luciano's problem.

I know. He is my friend a long time and so I wrote more information, just that.

Regars,

Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Juha Manninen
In reply to this post by Marcos Douglas B. Santos
On Sat, Nov 28, 2015 at 1:48 AM, Marcos Douglas <[hidden email]> wrote:
> Another tip: Factories resolve some problems but there is a cost. The
> factory will creates your instance (object) but it know only one
> constructor, ie, the base class constructor.

No, the constructor must then be virtual.
Inheritance is OK if all public virtual methods can be defined in an
abstract base class. If works as an interface then (without
"interface" keyword).
It does not work when you need TDog.Bark but it would work with
abstract function TAnimal.Says: String, which is implemented as "
Return 'wuff' " for TDoc and " Return 'miou' " for TCat.

But yes, the other answers are better for more complex cases.

Juha
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marcos Douglas B. Santos
On Sat, Nov 28, 2015 at 9:11 AM, Juha Manninen
<[hidden email]> wrote:
> On Sat, Nov 28, 2015 at 1:48 AM, Marcos Douglas <[hidden email]> wrote:
>> Another tip: Factories resolve some problems but there is a cost. The
>> factory will creates your instance (object) but it know only one
>> constructor, ie, the base class constructor.
>
> No, the constructor must then be virtual.

Even using virtual constructors, the factory will know only one
signature to instantiate objects.
AFAIK you only will know constructor or methods arguments, in runtime,
using RTTI, if you put the constructor/method as published. But, even
if you put it as published, you do not know the right context to call
the right constructor -- considering you have many constructors with
different arguments, of course.

> Inheritance is OK if all public virtual methods can be defined in an
> abstract base class. If works as an interface then (without
> "interface" keyword).

Inheritance breaks encapsulation. This was written even in GoF Patterns.
There are many post, on the web, explaining why inheritance is evil.
I searched on Google right now:
http://blog.berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/
http://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html

> It does not work when you need TDog.Bark but it would work with
> abstract function TAnimal.Says: String, which is implemented as "
> Return 'wuff' " for TDoc and " Return 'miou' " for TCat.

Completing things is the source of complexity, ie, completing
(abstract or virtual) methods. To complete methods in a sub class, you
need to know how super class works and this violate the (first tier
of) encapsulation.

Best regards,
Marcos Douglas

PS: Perhaps this video could be interesting
http://www.infoq.com/presentations/Simple-Made-Easy
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Serguei TARASSOV
In reply to this post by Luciano de Souza
Hello,

What is an explanation why the first code is not good but the second one
is good?
At least, the first code is three times as shorter and clear.
Second code seems to be taken from Delphi 7 where the first one doesn't
works.

On 28/11/2015 12:00, [hidden email] wrote:

>> >if Animal is IBarkable then (Animal as IBarkable).Bark;
> This is not good usage of Interfaces. Use the interface variable instead.
>
> var
>    intf: IBarkable
> begin
>    if Supports(Animal, IBarbable, intf) then
>    begin
>      intf.Bark;
>      // you can continue here using intf further without casting
>    end;
--
Regards,
Serguei
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
--
Regards,
Serguei
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Juha Manninen
In reply to this post by Marcos Douglas B. Santos
On Sat, Nov 28, 2015 at 2:07 PM, Marcos Douglas <[hidden email]> wrote:
> Even using virtual constructors, the factory will know only one
> signature to instantiate objects.

Yes and often that is enough.
A constructor is then part of the API. (Lets call it API now to avoid
confusion with the "interface" keyword.)

> Completing things is the source of complexity, ie, completing
> (abstract or virtual) methods. To complete methods in a sub class, you
> need to know how super class works and this violate the (first tier
> of) encapsulation.

Yes, if the super class does something useful by itself, then often
your class design is wrong.
My example was about an abstact base class that only provides an API,
the derived classes implement it. The concept is similar to an
interface definition, but often the implementation is more compact and
easier to read.
Interface can define only methods. An abstract base class can have
also variables and protected helper methods needed by the
implementation code.

We are partly talking about different things here. You and the articly
you linked advocate the "composition over inheritance" principle. Yes,
it is a good principle. I also advocate it, but I also say that using
abstract base classes for APIs is often a cleaner and easier way
compared to interface types.
Look at the Lazarus API defined in IDEIntf package. It uses abstract
base classes and works well.

Abstract base classes do not work in a multiple inheritance case. Then
an interface type must be used.
However it is very rarely needed when the "composition over
inheritance" principle is followed.

Juha
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Marco van de Voort
In reply to this post by Marcos Douglas B. Santos
In our previous episode, Marcos Douglas said:
>
> Inheritance breaks encapsulation. This was written even in GoF Patterns.
> There are many post, on the web, explaining why inheritance is evil.
> I searched on Google right now:
> http://blog.berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/
> http://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html

Such rants are in direct opposition of GoF. GoF never meant to push a set of
unbendable rules. They just wanted to standarize jargon, and describe
somethings they encountered (without any claim that everybody should use
it).

Aside from the fact that interfaces is still inheritance.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Michael Van Canneyt
In reply to this post by Marcos Douglas B. Santos


On Sat, 28 Nov 2015, Marcos Douglas wrote:

> Inheritance breaks encapsulation. This was written even in GoF Patterns.
> There are many post, on the web, explaining why inheritance is evil.
> I searched on Google right now:
> http://blog.berniesumption.com/software/inheritance-is-evil-and-must-be-destroyed/
> http://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html
>

A perfect example why 90% of internet (and in particular blogs) are a waste of bandwidth.

From the silly-example-department: (a close cousin of the "ministry of silly walks")

A dog *is* an animal, a dog does not *have* an animal.

However (and now it gets tricky):
A human can have an animal (e.g. a dog), but she *is* also an animal.

On top of that, (s)he can inherit things from his ancestors.
(for example AIDS or some other nasty disease. Or several millions of dollars)

!! OMG !! Maybe we just discovered a new form of multiple inheritance...
Definite proof we should all use C++, or maybe it is time for C+++ !!

Please...

There is no golden rule which encapsulates (sic) all situations.

People should use their brain and choose a pattern or methodology judiciously,
instead of wasting other people's time with stupid rants on blogs.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Graeme Geldenhuys-6
On 2015-11-28 19:34, Michael Van Canneyt wrote:
> !! OMG !! Maybe we just discovered a new form of multiple inheritance...
> Definite proof we should all use C++, or maybe it is time for C+++ !!

ROFL - you just made my day. :-)


> There is no golden rule which encapsulates (sic) all situations.

+1


Regards,
  - Graeme -

--
fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal
http://fpgui.sourceforge.net/

My public PGP key:  http://tinyurl.com/graeme-pgp
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Implementing Factory Method with Pascal

Inoussa OUEDRAOGO
In reply to this post by Michael Van Canneyt
> There is no golden rule which encapsulates (sic) all situations.

+1

> People should use their brain and choose a pattern or methodology
> judiciously,

+1, after all that is  what engineering is all about.


--
Inoussa O.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
12