RESPONSIBILITY See also: Accountability Holding components accountable Giving objects explicit responsibilities 1:1 Object:Responsibility Ratio (when possible) Responsibilty to code Responsibilty to team Responsibilty to your future self
Alan Kay Father of OOP "I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages"
OBJECT-ORIENTED PROGRAMMING Originated from Functional and Procedural Solution to code organization, data-hiding, reuse Solution to easier testing Solution to structure and protocols
OOP: OBJECT ORIENTED PHP PHP 5.0 - 5.2.X ENHANCEMENTS Pass-by-reference p u b l i c / p r i v a t e / p r o t e c t e d visibility Static methods/properties Class constants __construct() Destructors Abstract and Final classes Interfaces
PROCEDURAL < ? p h p f u n c t i o n g e t _ d b ( ) { / * r e t u r n t h e d b c o n n e c i t o n * / } f u n c t i o n u s e r _ s h o w ( $ u s e r ) { / * A w e s o m e p r o c e d u r a l c o d e * / } f u n c t i o n u s e r _ f e t c h ( $ u s e r ) { $ d b = g e t _ d b ( ) ; / * A w e s o m e p r o c e d u r a l c o d e * / f u n c t i o n u s e r _ d e l e t e ( $ u s e r ) { $ d b = g e t _ d b ( ) ; / * A w e s o m e p r o c e d u r a l c o d e * / f u n c t i o n u s e r _ c r e a t e ( $ u s e r , $ d a t a ) { $ d b = g e t _ d b ( ) ; / * A w e s o m e p r o c e d u r a l c o f u n c t i o n u s e r _ u p d a t e ( $ u s e r , $ d a t a ) { $ d b = g e t _ d b ( ) ; / * A w e s o m e p r o c e d u r a l c o f u n c t i o n u s e r _ s t o r e _ d a t a ( $ u s e r , $ u s e r _ d a t a ) { i f ( u s e r _ f e t c h ( $ u s e r ) ) { r e t u r n u s e r _ u p d a t e ( $ u s e r , $ u s e r _ d a t a ) ; } e l s e { r e t u r n u s e r _ c r e a t e ( $ u s e r , $ u s e r _ d a t a ) ; }
ABSTRACTED < p h p c l a s s U s e r e x t e n d s E l o q u e n t { / / A l l p r o v i d e d b y E l o q u e n t p u b l i c s t a t i c f u n c t i o n f i n d ( $ i d ) { } p u b l i c f u n c t i o n d e l e t e ( ) { } p u b l i c f u n c t i o n c r e a t e ( $ a t t r i b u t e s ) { } p u b l i c f u n c t i o n u p d a t e ( $ a t t r i b u t e s ) { } p u b l i c f u n c t i o n s a v e ( ) { } }
ENCAPSULATION Keep data and behavior in a black box Use a public interface to interact with the black box Much like a function - data in, data out Prevents leaky abstractions
EXAMPLE - PLAIN CLASSES < ? p h p c l a s s R e c t a n g l e { p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ c o o r d s ) { } p u b l i c f u n c t i o n g e t C o o r d i n a t e s ( ) { } p u b l i c f u n c t i o n s e t C o o r d i n a t e s ( $ c o o r d s ) { } p u b l i c f u n c t i o n g e t X X ( ) { } p u b l i c f u n c t i o n g e t X Y ( ) { } p u b l i c f u n c t i o n g e t Y X ( ) { } p u b l i c f u n c t i o n g e t Y Y ( ) { } p u b l i c f u n c t i o n s e t X X ( $ c o o r d ) { } p u b l i c f u n c t i o n s e t X Y ( $ c o o r d ) { } p u b l i c f u n c t i o n s e t Y X ( $ c o o r d ) { } p u b l i c f u n c t i o n s e t Y Y ( $ c o o r d ) { }
EXAMPLE - INHERITED < ? p h p c l a s s S q u a r e e x t e n d s R e c t a n g l e { } c l a s s D i a m o n d e x t e n d s R e c t a n g l e { } c l a s s R e c t a n g l e { p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ c o o r d s ) { } p u b l i c f u n c t i o n g e t C o o r d i n a t e s ( ) { } p u b l i c f u n c t i o n s e t C o o r d i n a t e s ( $ c o o r d s ) { } p u b l i c f u n c t i o n g e t X X ( ) { } p u b l i c f u n c t i o n g e t X Y ( ) { } p u b l i c f u n c t i o n g e t Y X ( ) { } p u b l i c f u n c t i o n g e t Y Y ( ) { } p u b l i c f u n c t i o n s e t X X ( $ c o o r d ) { }
POLYMORPHISM Probably the best feature of OOP Any object may be used if it implements expected method/property Allows code branching without if/else logic PHP is a duck typed language - "If it walks like a duck" PHP also allows type hinting - "You will be a duck and you will like it!"
EXAMPLE < p h p c l a s s B l o g P o s t W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } } c l a s s G a l l e r y W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } } c l a s s P o l l W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } }
EXAMPLE W/ TYPE HINTING < p h p i n t e r f a c e W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } } c l a s s B l o g P o s t W i d g e t i m p l e m e n t s W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } } c l a s s G a l l e r y W i d g e t i m p l e m e n t s W i d g e t { p u b l i c f u n c t i o n r e n d e r ( ) { } } c l a s s P o l l W i d g e t i m p l e m e n t s W i d g e t
CODE REUSE AND EXTENSIBILITY Proper abstraction leads to reusable code Fluent Class in Laravel 3 as an example (not the DB one) Validators in Laravel Drivers
LATE-STATIC BINDING s e l f never worked like it should've s t a t i c is what s e l f wanted to be s e l f will call the class the method was defined on, not the subclass you likely expecting s t a t i c however will
EXAMPLE < p h p c l a s s A { p u b l i c f u n c t i o n s a d f a c e ( ) { r e t u r n n e w s e l f ( ) ; } p u b l i c f u n c t i o n h a p p y f a c e ( ) { r e t u r n n e w s t a t i c ( ) ; } } c l a s s B e x t e n d s A { } $ b = n e w B ( ) ; $ b - > s a d f a c e ( ) ; / / A o b j e c t
TRAITS Traits allow for methods/properties to be added to a class at compile-time Not quite mixins, but close In a sense, allows for multiple inheritance This scares people Not always the right solution, but often suited Another great form of reusable abstraction
EXAMPLE < p h p t r a i t B a r { p r o t e c t e d $ b a z ; p u b l i c f u n c t i o n b a z ( ) { r e t u r n $ t h i s - > b a z ; } } c l a s s F o o { u s e B a r ; p u b l i c f u n c t i o n s e t B a z ( $ b a z ) {
COMPOSITION OVER INHERITANCE Composing two or more objects together The sum is greater than the parts Provides more flexibility Not always the right solution, but often the better suited More code to compose (usually) "Does X belong on this object?"
COMPOSITION WITH TRAITS SOME PEOPLE REALLY HATE TRAITS Traits allow for methods/properties to be added to a class at compile-time Not quite mixins, but close In a sense, allows for multiple inheritance This scares people Not always the right solution, but often the better suited Another great form of reusable abstraction
TDD SERVES MANY PURPOSES Guiding you to make well abstracted solutions Help you write the least code possible Ensure your code works Ensure your code works as it is intended Ensure tests become executable documentation for your code and acceptance criteria
LISTENING TO TESTS Tests will let you know if somethign smells funny A class that does too many things is hard to test A test with too many dependencies is hard to test A test with too many mocks is hard to mantain A test that is too difficult is easy to abandon
EXAMPLE - AUTHORITY < p h p p r o t e c t e d $ d i s p a t c h e r ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ c u r r e n t U s e r , $ d i s p a t c h e r = n u l l ) { $ t h i s - > r u l e s = n e w R u l e R e p o s i t o r y ; $ t h i s - > s e t D i s p a t c h e r ( $ d i s p a t c h e r ) ; $ t h i s - > s e t C u r r e n t U s e r ( $ c u r r e n t U s e r ) ; $ t h i s - > d i s p a t c h ( ' a u t h o r i t y . i n i t i a l i z e d ' , a r r a y ( ' u s e r ' = > $ t h i s - > g e t C u r r e n t U s e r ( ) , ) ) ; } p u b l i c f u n c t i o n d i s p a t c h ( $ e v e n t N a m e , $ p a y l o a d = a r r a y ( ) ) { i f ( $ t h i s - > d i s p a t c h e r ) { r e t u r n $ t h i s - > d i s p a t c h e r - > f i r e ( $ e v e n t N a m e , $ p a y l o a d ) ;
EXAMPLE - AUTHORITY TESTS < p h p p u b l i c f u n c t i o n t e s t I n i t i a l i z e E v e n t ( ) { $ t e s t = n e w s t d C l a s s ; $ u s e r = n e w s t d C l a s s ; $ u s e r - > n a m e = ' T e s t e r ' ; $ t h i s - > d i s p a t c h e r - > l i s t e n ( ' a u t h o r i t y . i n i t i a l i z e d ' , f u n c t i o n ( $ p a y l o a d $ t e s t - > u s e r = $ p a y l o a d - > u s e r ; $ t e s t - > t i m e s t a m p = $ p a y l o a d - > t i m e s t a m p ; } ) ; $ a u t h o r i t y = n e w A u t h o r i t y ( $ u s e r , $ t h i s - > d i s p a t c h e r ) ; $ t h i s - > a s s e r t S a m e ( $ t e s t - > u s e r , $ u s e r ) ; $ t h i s - > a s s e r t I n s t a n c e O f ( ' D a t e T i m e ' , $ t e s t - > t i m e s t a m p ) ; }
SOME THINGS ON TESTING You do not need a test for every method Sometimes you need more than one test for a method Mocking can fall short and lose sync. If you don't test everything, test areas of high churn Do not reach for AspectMock right away because something is hard Test your own code, not third party
SOME MORE THINGS ON TESTING This is not a good test < p h p t e s t F o o ( ) { $ f o o = n e w F o o ( ) ; $ b a r = n e w B a r ( ) ; $ b a z = $ f o o - > d o S o m e t h i n g E p i c ( $ b a r ) ; i f ( i s _ a r r a y ( $ b a z ) a n d ! i s _ e m p t y ( $ b a z ) ) { $ t h i s - > a s s e r t T r u e ( t r u e ) ; } e l s e { $ t h i s - > a s s e r t F a l s e ( t r u e ) ; } }
SOME MORE THINGS ON TESTING Nor this < p h p t e s t S o m e t h i n g ( ) { $ f o o = n e w F o o ( ) ; $ b a r = n e w B a r ( ) ; $ b a z = $ f o o - > d o S o m e t h i n g E p i c ( $ b a r ) ; / / B a z d o e s n ' t s e e m t o e v a l r i g h t , p a s s i n g f o r n o w / / @ T O D O : C o m e b a c k l a t e r $ t h i s - > a s s e r t T r u e ( t r u e ) ; }
SOME MORE THINGS ON TESTING Nor this < p h p t e s t S t r i n g R e v e r s e ( ) { $ s t r = n e w A w e s o m e S t r i n g ( ) ; $ s t r i n g = ' H e l l o ' ; / / I m p l e m e n t a t i o n : r e t u r n s t r r e v ( $ s t r i n g ) $ r e v e r s e d = $ s t r - > r e v e r s e S t r i n g ( $ s t r i n g ) ; $ t h i s - > a s s e r t E q u a l s ( s t r r e v ( $ s t r i n g ) , $ r e v e r s e d ) ; }
SOME MORE THINGS ON TESTING Nor this...I don't even... < p h p t e s t W e i r d F e a t u r e ( ) { / / 2 + 2 s h o u l d b e 4 i f ( 2 + 2 = = 3 ) { $ t h i s - > a s s e r t T r u e ( t r u e ) ; } e l s e { $ t h i s - > a s s e r t T r u e ( f a l s e ) ; } }
RESPONSIBILITY: OBJECTS Give your objects a task Do not overwork your objects Give your objects some proper tests Like profiling and logging, objects can help with accountability
RESPONSIBILITY: YOU You have a responsibility to yourself You have a responsibility to your team You have a responsibility to your consumers You have a responsibility to your future self Become responsible and be accountable for your code