On Github jacobwalker0814 / implementing-oop
By: Jacob Walker
Object Oriented Programming
150+ languages with different degrees / implementations. *
This talk focuses on PHP and Ruby.
Goal is to be literate in OOP verbiage
A class defines the information and behavior of a thing.
An object is a specific instance of a class.
If Developer is a class...
Then Jacob and Seth are objects or instances of class Developer.
class Developer { }
class Developer end
$jacob = new Developer;
seth = Developer.new
A function on a class.
Provides access to data and/or performs actions (behavior).
class Developer { function code() { print "1337 c0d3r"; } } $jacob = new Developer; $jacob->code(); // Output: 1337 c0d3r
class Developer def code puts "1337 c0d3r" end end seth = Developer.new seth.code # Output: 1337 c0d3r
A method that is automatically called for new instances.
class Developer { function __construct() { print "Hello, world!"; } } $jacob = new Developer; // Output: Hello, world!
class Developer def initialize puts "Hello, world!" end end seth = Developer.new # Output: Hello, world!
Defines the data about the object.
class Developer { function __construct($name) { $this->name = $name; } function greet() { print "Hi, I'm " . $this->name . "."; } } $jacob = new Developer("Jacob"); $jacob->greet(); // Output: Hi, I'm Jacob.
class Developer def initialize name @name = name end def greet puts "Hi, I'm #{@name}." end end seth = Developer.new("Seth") seth.greet # Output: Hi, I'm Seth.
One class can extend another class. The child class inherits properties and methods of the parent class.
class Coder { function code() { print "Code!"; } } class Developer extends Coder { function develop() { print "Develop!"; } } $jacob = new Developer; print $jacob->code(); // Output: Code! print $jacob->develop(); // Output: Develop!
class Coder def code puts "Code!" end end class Developer < Coder def develop puts "Develop!" end end seth = Developer.new seth.code # Output: Code! seth.develop # Output: Develop!
A child class inherits the type of all parent classes.
class Coder { } class Developer extends Coder { } $dev = new Developer; $coder = new Coder; $dev instanceof Coder && print "Dev is a Coder"; $dev instanceof Developer && print "Dev is a Developer"; $coder instanceof Coder && print "Coder is a Coder"; $coder instanceof Developer || print "Coder is not a Developer";
class Human {} // Human // | class Coder extends Human {} // Coder // / \ class Developer extends Coder {} // / \ // / \ class Designer extends Coder {} // Developer Designer
Control who can get to properties / methods (visibility).
Different meanings in different languages.
We'll cover PHP then Ruby
Control who can get to properties / methods.
class Developer { public $name; private $secret = "Foo"; } $jacob = new Developer; $jacob->name = "Jacob"; print $jacob->name; // Output: Jacob $jacob->secret = "Bar"; // PHP Fatal error: Cannot access private property Developer::$secret print $jacob->secret; // PHP Fatal error: Cannot access private property Developer::$secret
Getters and Setters
class Developer { private $name; public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } } $jacob = new Developer; $jacob->setName("Jacob"); print $jacob->getName(); // Output: Jacob
class Coder { private $code_style; protected $name; } class Developer extends Coder { // Can access $this->name but not $this->code_style }
class Developer { public function greet() { print "Hello there!"; } private function revealSecret() { print "I code in MS Word"; } } $jacob = new Developer; $jacob->greet(); // Output: Hello there! $jacob->revealSecret(); // PHP Fatal error: Call to private method Developer::revealSecret()
class Coder { private function secret() { return "secret"; } protected function kind_of_secret() { return "slightly less secret"; } } class Developer extends Coder { // Can call kind_of_secret but not secret } // Can not call either method
Control who can call methods.
Ruby properties can not be accessed directly from outside a class. Make available through methods.
class Developer def initialize @secret = "Foo" end def name @name end def name=(name) @name = name end end seth = Developer.new seth.name = "Seth" puts seth.name # Output: Seth puts seth.secret # NoMethodError: undefined method `secret'
Ruby provides shorthands
class Developer attr_accessor :name # Makes a getter and setter attr_reader :name # Makes a getter attr_writer :name # Makes a setter def initialize @secret = "Foo" end end seth = Developer.new seth.name = "Seth" puts seth.name # Output: Seth
class Developer def greet puts "Hello there!" end private def revealSecret puts "I code in MS Word" end end seth = Developer.new seth.greet # Output: Hello there! seth.revealSecret # NoMethodError: private method `revealSecret' called
class Developer def greet puts "Hello there!" end def revealSecret puts "I code in MS Word" end private :revealSecret end
Unlike in PHP, private methods available to children
class Coder attr_accessor :password private :password def initialize password @password = password end end class Developer < Coder def revealPassword password end end seth = Developer.new("Banana") puts seth.revealPassword # Banana puts seth.password # NoMethodError private method `password' called
class Developer attr_reader :hacks, :power protected :power private :hacks def initialize hacks, power @hacks = hacks @power = power end def is_stronger_than? other_dev power > other_dev.power end def is_hackier_than? other_dev hacks > other_dev.hacks end end seth = Developer.new(83, 9001) jacob = Developer.new(174, 8999) seth.is_stronger_than? jacob # true seth.is_hackier_than? jacob # NoMethodError: private method `hacks' called
A child class can override or redefine a method that was defined in a parent class
class Coder { public function type() { print "I type like this!"; } } class Developer extends Coder { public function type() { print "JK, I type like this!"; } } $jacob = new Developer; $jacob->type(); // Output: JK, I type like this!
Can call your parent's version of a method by using the parent:: prefix.
class TestingBase extends PHPUnit_Framework_TestCase { protected $logger; public function setUp() { $this->logger = new Logger(); } } class PayrollTest extends TestingBase { protected $payroll_engine; public function setUp() { parent::setUp(); $this->payroll_engine = new Payroll_Engine(); } }
Use super to call the same method in a parent class.
class Dancer def dance puts "Boogie!" end end class DiscoDancer < Dancer def dance super puts "Boogie Woogie!" end end seth = DiscoDancer.new seth.dance # Output: Boogie! Boogie Woogie!
Side note, super will automatically pass all args unless you specify otherwise
Ruby can redefine methods at run time
class Developer def code puts "Bits!" end end # Some time later class Developer def code puts "Hacks!" end end seth = Developer.new seth.code # Output: Hacks!
In PHP a method marked final can not be overriden by subclasses. Use with caution.
class Foo { final public function bar() { } }
Final methods are not natively supported in Ruby as it goes against the spirit of the language.
class Developer { const EDITOR_VIM = "vim"; const EDITOR_EMACS = "emacs"; } print Developer::EDITOR_VIM; // Output: vim print Developer::EDITOR_EMACS; // Output: emacs
class Developer EDITOR_VIM = "vim"; EDITOR_EMACS = "emacs"; end puts Developer::EDITOR_VIM; # Output: vim puts Developer::EDITOR_EMACS; # Output: emacs
Properties / methods that are associated with a class, not any particular instance.
From within a class, access with static:: or self::
Use class name when outside of the class
class Developer { public static $devs_made = 0; public function __construct() { static::$devs_made++; } } print Developer::$devs_made; // Output: 0 $jacob = new Developer; print Developer::$devs_made; // Output: 1 $matthew = new Developer; print Developer::$devs_made; // Output: 2
class Config { private static $instance; public static function getInstance() { return static::$instance ?: new static(); } // Only this class can call "new Config" private function __construct() {} } $config = Config::getInstance(); $same_config = Config::getInstance();
class Developer @@devs_made = 0 # Class variable def self.devs_made # Class method @@devs_made end def initialize @@devs_made += 1 end end seth = Developer.new puts Developer.devs_made # Output: 1 jarrett = Developer.new puts Developer.devs_made # Output: 2
A class can be declared abstract which means it can not be directly instantiated. Another class must extend it.
Methods on an abstract class can also be declared abstract which means a child class must implement them.
abstract class Model { abstract public function getTable(); public function formattedTable() { return strtoupper($this->getTable()); } } class User extends Model { public function getTable() { return "users"; } } $joe = new User; print $joe->formattedTable(); // Output: USERS
Not directly supported. Can simulate with some hackery if needed, but it doesn't really fit the style of Ruby.
interface WordTyper { public function type(); } class Designer implements WordTyper { public function type() { print "CSS!"; } } class Developer implements WordTyper { public function type() { print "PHP!"; } }
if ($object instanceof WordTyper) { $object->type(); }
Writing software to run an automated pizza joint
Starting with classes Pizza and Oven
class Pizza { } class Oven { public function bake(Pizza $pizza) { $bake_time = 17; $bake_temp = 425; } }
Boss decides to maximize oven time by offering cakes
abstract class Food { abstract public function getBakeTime(); abstract public function getBakeTemp(); } class Cake extends Food { public function getBakeTime() { return 30; } public function getBakeTemp() { return 350; } } class Pizza extends Food { public function getBakeTime() { return 17; } public function getBakeTemp() { return 425; } }
class Oven { public function bake(Food $food) { $bake_time = $food->getBakeTime(); $bake_temp = $food->getBakeTemp(); } }
Boss acquire the pottery / craft business next door
Need to expand our software to run the kiln.
interface Bakeable { public function getCookTime(); public function getCookTemp(); } class ClayPot implements Bakeable { public function getCookTime() { return 300; } public function getCookTemp() { return 950; } } class Cake implements Bakeable { public function getCookTime() { return 18; } public function getCookTemp() { return 350; } } class Pizza implements Bakeable { public function getCookTime() { return 18; } public function getCookTemp() { return 350; } }
class Oven { public function bake(Bakeable $bakeable) { $cook_time = $bakeable->getCookTime(); $cook_temp = $bakeable->getCookTemp(); } }