=pod

=encoding utf-8

=head1 NAME

Zydeco::Manual::04_Factories - Factories to help your objects make other objects

=head1 SYNOPSIS

  package MyApp {
    use Zydeco;
    
    class Person {
      has name = "Anonymous";
      has gender;
      
      factory new_man ( Str $name ) {
        return $class->new(name => $name, gender => 'm');
      }
      
      factory new_woman ( Str $name ) {
        return $class->new(name => $name, gender => 'f');
      }
    }
  }
  
  my $alice = MyApp->new_woman("Alice");
  my $bob   = MyApp->new_man("Bob");

=head1 MANUAL

Traditionally in Perl, if you define a class "MyApp::Person", you would
instantiate objects using C<< MyApp::Person->new(%args) >>.

In Zydeco, you use C<< MyApp->new_person(%args) >> instead. This means that
there is one central package ("MyApp" in the example) which constructs all
your objects.

Having all your objects instantiated through one package makes it easy to
control which objects are constructed from one central place. For example,
if all of your code is using C<< MyApp->new_database_connection >> to get
a connection to your database, you can override that method to insert a
connection to a testing copy of your database when testing your app.

=head2 The Default Factory Method

When you define a class, a factory method is created for you by default.

  package MyApp {
    use Zydeco;
    class Person;
  }
  
  # Here's the factory method being used:
  my $bob = MyApp->new_person(%args);

The default factory method just passes on any arguments to your class's
constructor. In this example, that would be C<< MyApp::Person->new(%args) >>.
Because Zydeco uses L<Moo> to build your class, you can use the standard
Moo "BUILD" and "BUILDARGS" methods to alter tweak object construction.

=head2 Renaming the Default Factory Method

By default, the factory method is the same as the class name, but lower-cased,
with "::" replaced by "_", and with "new_" as a prefix. So the class
"Vehicle::Car::Electric" will have a factory method called
"new_vehicle_car_electric". You may wish to rename the factory method:

  package MyApp {
    use Zydeco;
    ...;
    
    class Vehicle::Car::Electric {
      factory new_electric_car;
    }
  }

=head2 Defining Custom Factory Methods

The C<factory> keyword can also be used with a block and optionally a
signature to offer more control over object construction.

  package MyApp {
    use Zydeco;
    
    class Person {
      has name = "Anonymous";
      has gender;
      
      factory new_man ( Str $name ) {
        return $class->new(name => $name, gender => 'm');
      }
      
      factory new_woman ( Str $name ) {
        return $class->new(name => $name, gender => 'f');
      }
    }
  }

In this example, we create two factory methods for the "Person" class.
If a class has custom factory methods, then the default one ("new_person")
won't be made. The custom factory methods are defined I<< instead of >>
the default one, not I<< as well as >>.

If you need the default one too, this is pretty simple.

  package MyApp {
    use Zydeco;
    
    class Person {
      has name = "Anonymous";
      has gender;
      
      factory new_man ( Str $name ) {
        return $class->new(name => $name, gender => 'm');
      }
      
      factory new_woman ( Str $name ) {
        return $class->new(name => $name, gender => 'f');
      }
      
      factory new_person;    # default factory method
    }
  }

Within these methods, the variable C<< $class >> is defined, which holds
the class being constructed as a string. The variable C<< $factory >> is
the factory package as a string. This allows you to easily construct other
objects which may be needed by the current object.

  package MyApp {
    use Zydeco;
    
    class Car {
      has wheels = [];
      has colour;
      
      factory new_four_wheeler ( $colour ) {
        $class->new(
          colour => $colour,
          wheels => [
            $factory->new_wheel,
            $factory->new_wheel,
            $factory->new_wheel,
            $factory->new_wheel,
          ],
        );
      }
    }
    
    class Wheel;
  }

A key feature of custom factory methods is that although they are I<logically>
part of the factory package, they are I<lexically> defined within the class,
so have access to its lexical variables.

=head2 Factory Methods via a Proxy

It is possible to instead define additional methods in the class itself and
then install factory methods that call those.

  package MyApp {
    use Zydeco;
    
    class Person {
      has name = "Anonymous";
      has gender;
      
      method new_man ( Str $name ) {
        return $class->new(name => $name, gender => 'm');
      }
      
      factory new_guy via new_man;
      
      method new_woman ( Str $name ) {
        return $class->new(name => $name, gender => 'f');
      }
      
      factory new_gal via new_woman;
    }
  }

Inside C<method> blocks, a C<< $factory >> variable is available, which is
preset to C<< $self->FACTORY >>.

One advantage of using a proxy is that subclasses can inherit the method and
potentially override it.

=head2 Singletons

Factories make it pretty easy to implement the singleton pattern.

  package MyApp {
    use Zydeco;
    
    class AppConfig {
      ...;
       
      factory get_appconfig () {
        state $config = $class->new();
      }
    }
  }

Now C<< MyApp->get_appconfig >> will always return the same AppConfig object.
Because any explicit use of the factory keyword in a class definition
suppresses the automatic creation of a factory method for the class, there
will be no C<< MyApp->new_appconfig >> method for creating new objects of
that class.

(People can still manually call C<< MyApp::AppConfig->new >> to get a new
AppConfig object, but remember Zydeco discourages calling constructors
directly, and encourages you to use the factory package for instantiating
objects!)

=head2 Abbreviated Syntax

Like with regular methods, the abbreviated syntax is allowed.

  factory new_man   ($n) = $class->new(name => $n, gender => 'm');
  factory new_woman ($n) = $class->new(name => $n, gender => 'f');

=head1 KEYWORDS

In this chapter, we looked at the following keyword:

=over

=item C<< factory >>

=back

=head1 NEXT STEPS

We have already looked at how to define methods, but now let's look at
multimethods, which can sometimes be a more elegant way to define methods.

=over

=item *

L<Zydeco::Manual::05_Multimethods> - multi methods

=back

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2020 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

