Object-Oriented Programming: Why You're Doing It Wrong Toby Inkster - - PowerPoint PPT Presentation

object oriented programming why you re doing it wrong
SMART_READER_LITE
LIVE PREVIEW

Object-Oriented Programming: Why You're Doing It Wrong Toby Inkster - - PowerPoint PPT Presentation

Object-Oriented Programming: Why You're Doing It Wrong Toby Inkster Three weird tricks to make your object-oriented code more encapsulated, more reusable, and more maintainable. Toby Inkster (TOBYINK) Type::Tiny MooX::late Moops /


slide-1
SLIDE 1

Object-Oriented Programming: Why You're Doing It Wrong

Toby Inkster

Three weird tricks to make your object-oriented code more encapsulated, more reusable, and more maintainable.

slide-2
SLIDE 2

Toby Inkster (TOBYINK)

  • Type::Tiny
  • MooX::late
  • Moops / Kavorka
  • Test::Modern
  • Pry
  • Object::Util
  • PerlX::Maybe
  • Syntax::Collector
slide-3
SLIDE 3

Object-Oriented Programming

  • Examples in this presentation use Moo.
  • Moo is a lightweight version of Moose.
  • Most of these examples can be rewritten to use Moose with
  • nly minor changes.
  • Moo is still Perl
  • You could implement any of this with just core Perl OO if you

were so inclined.

slide-4
SLIDE 4

Stop creating mutable objects

http://www.diylol.com/

slide-5
SLIDE 5

Stop creating mutable objects

  • Perl Best Practices

recommends creating methods called get_foo and set_foo.

my $obj = Pony->new(name => 'Pinkie Pie'); $obj->set_name('Twilight Sparkle'); say $obj->get_name();

slide-6
SLIDE 6

Stop creating mutable objects

  • Perl Best Practices

recommends creating methods called get_foo and set_foo.

  • Moose standard practice is to

have a single accessor called foo that allows you to either get

  • r set the attribute value.

my $obj = Pony->new(name => 'Pinkie Pie'); $obj->name('Twilight Sparkle'); say $obj->name();

slide-7
SLIDE 7

Stop creating mutable objects

  • Perl Best Practices

recommends creating methods called get_foo and set_foo.

  • Moose standard practice is to

have a single accessor called foo that allows you to either get

  • r set the attribute value.
  • These are both wrong.
slide-8
SLIDE 8

Stop creating mutable objects

my $alice = Person->new( name => 'Alice', best_pony => Pony->new(name => 'Twilight Sparkle'), ); my $bob = Person->new( name => 'Bob', best_pony => $alice->best_pony(), # It's what brought us together ); $alice->best_pony->set_name('Sunset Shimmer'); say $bob->best_pony->get_name(); # Spooky action at a distance

slide-9
SLIDE 9

Stop creating mutable objects

my $conference = Event->new( start => DateTime->new(...) ); my $keynote = Event->new( start => $conference->start ); # We need the keynote to be at the end of the conference $keynote->start->add(seconds => 5*60*60); # D'oh! print $conference->start, "\n";

slide-10
SLIDE 10

Stop creating mutable objects

  • Make your accessors read-only.
  • Don't allow an object's attribute values to be changed

after it's been constructed.

  • Save yourself from spooky action at a distance.
slide-11
SLIDE 11

Stop creating mutable objects

  • Moose and Moo:

is => 'ro'

  • Plain old Perl:

sub foo { $_[0]{foo} }

slide-12
SLIDE 12

Stop creating mutable objects

  • Make your accessors read-only.
  • Don't allow an object's attribute values to be changed

after it's been constructed.

  • Save yourself from spooky action at a distance.
  • Sometimes you really need to model a changing world.
slide-13
SLIDE 13

Stop creating mutable objects

my $alice = Person->new( name => 'Alice', best_pony => Pony->new(name => 'Twilight Sparkle'), ); my $bob = Person->new( name => 'Bob', best_pony => $alice->best_pony(), ); $alice->best_pony->set_name('Princess Twilight Sparkle'); # SPOILER ALERT! say $bob->best_pony->get_name();

slide-14
SLIDE 14

Stop creating mutable objects

package Pony { use Moo; has name => ( is => 'ro', writer => 'rename', ); } # Better than name() or set_name() because it's clear that this is # a method. It's a verb. It 'does something'. $pony->rename('Princess Twilight Sparkle');

slide-15
SLIDE 15

Stop creating mutable objects

  • If you really need to model a changing world:
  • Make attributes mutable:

– Only after careful consideration, not by default! – Not if they are part of the object's intrinsic identity.

  • Consider naming the writer method something that doesn't

sound like an attribute.

slide-16
SLIDE 16

Stop writing 'private' methods

We can actually see you.

https://www.flickr.com/photos/a_gods_child/4553482717/

slide-17
SLIDE 17

Stop writing 'private' methods

  • Methods named with a leading underscore are not really

private.

  • Subclasses can call them.
  • Subclasses can override them.
  • Even accidentally!
slide-18
SLIDE 18

Stop writing 'private' methods

package Employee { use Moo; has name => (is => 'ro'); sub _type { 'employee' } sub output { shift; say @_ } sub introduce_myself { my $self = shift; $self->output( 'My name is ', $self->name, ' and I am an ', $self->_type, ); } }

slide-19
SLIDE 19

Stop writing 'private' methods

package Typist { use Moo; extends 'Employee'; ...; } my $obj = Typist->new(name => 'Moneypenny'); $obj->introduce_myself(); Can't call method "press_button" on an undefined value at Typist.pm line 16

slide-20
SLIDE 20

Stop writing 'private' methods

package Typist { use Moo; extends 'Employee'; has default_keyboard => (is => 'lazy', builder => sub { Keyboard->new }); sub output { my $self = shift; my $text = join '', @_; $self->_type($self->default_keyboard, $text); } sub _type { my $self = shift; my ($kb, $text) = @_; for (my $i = 0; $i < length $text; $i++) { $kb->press_button( substr($text, $i, 1) ); } $kb->press_button('Enter'); } }

slide-21
SLIDE 21

Stop writing 'private' methods

  • How can we fix this?
slide-22
SLIDE 22

Stop writing 'private' methods

package Employee { use Moo; has name => (is => 'ro'); sub _type { 'employee' } sub output { shift; say @_ } sub introduce_myself { my $self = shift; $self->output( 'My name is ', $self->name, ' and I am an ', $self->_type, ); } }

slide-23
SLIDE 23

Stop writing 'private' methods

package Employee { use Moo; has name => (is => 'ro'); my $_type = sub { 'employee' }; sub output { shift; say @_ } sub introduce_myself { my $self = shift; $self->output( 'My name is ', $self->name, ' and I am an ', $self->$_type, ); } }

←a lexical method is just a coderef

slide-24
SLIDE 24

Stop writing 'private' methods

package Employee { use Moo; has name => (is => 'ro'); sub type { 'employee' } sub output { shift; say @_ } sub introduce_myself { my $self = shift; $self->output( 'My name is ', $self->name, ' and I am an ', $self->type, ); } }

←a public, documented method

slide-25
SLIDE 25

Stop writing 'private' methods

  • If a method is useful for end-users, then promote it to a

public method.

  • If a method exists in your namespace, then document it.
  • Otherwise, use 'lexical methods' – coderefs.
  • For lexical accessors, see Lexical::Accessor.
slide-26
SLIDE 26

Stop hard-coding stuff

Not a great idea.

http://www.diylol.com/

slide-27
SLIDE 27

Stop hard-coding stuff

package MyAuth; use Moo; sub fetch_user_list { my $self = shift; my $ua = LWP::UserAgent->new(); return $ua->get( "http://example.com/users.txt", ); }

slide-28
SLIDE 28

Stop hard-coding stuff

package MyAuth; use Moo; sub fetch_user_list { my $self = shift; my $ua = LWP::UserAgent->new(); return $ua->get( "http://example.com/users.txt", ); }

  • URL
  • User-agent
slide-29
SLIDE 29

Stop hard-coding stuff

package MyAuth; use Moo; sub fetch_user_list { my $self = shift; my $ua = LWP::UserAgent->new(); return $ua->get( "http://example.com/users.txt", ); } package MyAuth::Testing; use Moo; extends 'MyAuth'; sub fetch_user_list { my $self = shift; my $ua = LWP::UserAgent::WithLogging->new(); return $ua->get( "http://example.com/users.txt", ); }

slide-30
SLIDE 30

Stop hard-coding stuff

package MyAuth; use Moo; sub fetch_user_list { my $self = shift; my $ua = LWP::UserAgent->new(); return $ua->get( "http://example.com/users.txt", ); }

package MyAuth; use Moo; has user_agent => ( is => 'lazy', builder => sub { LWP::UserAgent->new() }, ); has user_list_url => ( is => 'lazy', builder => sub { "http://example.com/users.txt" }, ); sub fetch_user_list { my $self = shift; $self->user_agent->get($self->user_list_url); }

slide-31
SLIDE 31

Stop hard-coding stuff

package MyAuth::Testing; use Moo; extends 'MyAuth'; sub _build_user_agent { LWP::UserAgent::WithLogging->new(); } package MyAuth::Pony; use Moo; extends 'MyAuth'; sub _build_user_list_url { 'http://example.com/everypony.txt'; }

Look, it's really easy to subclass now!

slide-32
SLIDE 32

Stop hard-coding stuff

package MyAuth::Pony::Testing; use Moo; extends 'MyAuth'; sub _build_user_agent { LWP::UserAgent::WithLogging->new(); } sub _build_user_list_url { 'http://example.com/everypony.txt'; }

slide-33
SLIDE 33

Stop hard-coding stuff

  • Better for testing
  • Better for extensibility
slide-34
SLIDE 34

Stop hard-coding stuff

  • Things that you might be hard-coding without realising:
  • File paths

– Including the path to your config file

  • Object instances
  • Class names

– $class->new() is better than Class->new()

slide-35
SLIDE 35

Why you were doing it wrong

  • You created mutable objects
  • You wrote 'private' methods
  • You hard-coded stuff
slide-36
SLIDE 36

How to do it right

  • Create immutable objects
  • is => 'ro'
  • Avoid undocumented methods
  • If they seem useful enough, document them
  • Otherwise, make them coderefs so they stay private
  • Stop hard-coding stuff
  • is => 'lazy'
  • builder => sub { ... }
slide-37
SLIDE 37

That's all folks!