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();
}
}