Lecture 2 Object Orientation 1 / 51
Homework 1 Homework 1 was due at noon You will be graded on: Correctness: 15 points (passing all RSpec tests) Style: 5 points (having no Rubocop style offenses) Best Practices: 5 points (manually graded by TA) For this assignment, you will receive feedback but not actually lose any points Feedback by next Tuesday 2 / 51
How was HW 1? Vote at PollEv.com/CIS196776 3 / 51
O ce Hours https://www.seas.upenn.edu/~cis196/contact/ Likely final but changes will be announced if any come up 4 / 51
CIS19x lecture is after class 6-7:30pm in Towne 100 This week the focus will be Git/Github Next week is the last lecture and will focus on HTML/CSS basics Please att if you are uncomfortable with these subjects 5 / 51
Pry Gem The Pry Gem is a great way to debug your code After installing the gem, you add require 'pry' to the top of a file You can then add binding.pry anywhere in the file, acts like a breakpoint Whenever it reaches that point during execution, it will stop and give you a snapshot of the current state of your code You can execute methods, view currently defined variables, etc. Type exit to continue code execution 6 / 51
Making Pry Better with Pry-Nav Pry-Nav adds next, step, and continue commands to pry and allows you to step through your code No additional setup needed outside of installing the two gems pry-remote and pry-nav 7 / 51
Object Orientation 8 / 51
Classes & Objects Classes define clusters of behavior or functionality Almost everything in Ruby is an object Even types that are primitive in other languages, e.g. ints Because of this, there is always an object from which to call methods Even arithmetic operations are methods Ruby adds syntactic sugar to an expression like 1.+(2) to allow us to call it like 1 + 2 Convention is to use a method whenever you can 9 / 51
Creating a Class Use the class and keywords and place the class's code between them It is convention to use PascalCase when naming classes Create an instance of a class (an object) by calling the new method class Person person = Person.new p person.class #=> Person 10 / 51
Constructors If you define a method named initialize, it will be automatically executed as the constructor. class Person def initialize p 'Hi, I am being initialized!' Person.new #=> "Hi, I am being initialized!" 11 / 51
Constructors with Arguments Arguments passed into the new method are also passed to the constructor class Person def initialize(name) p "You've initialized a person with name: #{name}" Person.new('Sanj') #=> "You've initialized a person with name: Sanj" 12 / 51
Instance Variables Instance variables allow you to remember state in individual objects Their names always start with a single @ They are only visible to the instance to which they belong & every instance has its own set of instance variables No need to declare right at the top before the constructor, just define on the fly class Person def initialize(name) @name = name p "Assigned instance variable: #{@name}" Person.new('Sanj') #=> "Assigned instance variable: Sanj" 13 / 51
Instance Methods Methods that are inted to be called on a specific instance of the class Methods defined in a class are instance methods by default class Person def say_hello p 'Hello' person = Person.new person.say_hello #=> "Hello" 14 / 51
Instance Methods & Instance Variables Instance variables don't have to just be defined in the constructor You can also define instance variables in instance methods Instance variables can be accessed in any instance method regardless of where they've been defined 15 / 51
Instance Methods & Instance Variables class Person def set_name(name) @name = name def get_name @name person = Person.new person.set_name('sanj') p person.get_name #=> "Sanj" Note that the above is actually poor style in Ruby 16 / 51
Reader Methods What we call getter methods in other languages are referred to as reader methods in Ruby Used to return the value of a variable in a class Share name with variable being returned 17 / 51
Reader Methods Reader method has same name as the variable class Person def initialize(name) @name = name def name @name person = Person.new('Sanj') p person.name #=> "Sanj" 18 / 51
Writer Methods What we call setter methods in other languages are referred to as writer methods in Ruby Used to set the value of a variable in the class Naming convention is to share name with variable being set followed by = Ruby uses syntactic sugar to allow us to call the method like it's an assignment 19 / 51
Writer Methods class Person def initialize(name) @name = name def name @name def name=(name) @name = name person = Person.new('Sanj') person.name = 'Bob' p person.name #=> "Bob" 20 / 51
Writer Methods Note that writer methods can do more than just simple assignment class Person def name @name def name=(name) @name = name.length > 2? 'Too long' : name person = Person.new person.name = 'Sanjana' p person.name #=> "Too long" 21 / 51
Attr Readers The use of reader methods in Ruby is pretty common We can use the attr_reader method to automatically generate a reader method i.e. you don't have to define your own reader methods, they're generated on the fly class Person attr_reader :name def initialize(name) @name = name person = Person.new('Sanj') p person.name #=> "Sanj" 22 / 51
Attr Writers We can do the same with writers Using attr_writer will generate a writer method class Person attr_reader :name attr_writer :name person = Person.new person.name = 'Sanj' p person.name #=> "Sanj" 23 / 51
Attr Accessors We can generate both a reader and writer method in one line by using an attr_accessor class Person attr_accessor :name person = Person.new person.name = 'Sanj' p person.name #=> "Sanj" 24 / 51
Which will de ne a reader and writer? # A class Person def name @name def name=(name) @name = name # B class Person attr_reader :name attr_writer :name # C class Person attr_accessor :name # D: All of the above Vote at PollEv.com/CIS196776 25 / 51
Generating Multiple Methods Multiple symbols can be passed to all three of the attr methods This will generate a reader/writer (or both) for all of the symbols that you provide 26 / 51
Generating Multiple Methods class Person attr_reader :name, :age, :favorite_language def initialize(name, age, favorite_language) @name = name @age = age @favorite_language = favorite_language person = Person.new('Sanj', 21, 'Ruby') p person.name #=> "Sanj" p person.age #=> 21 p person.favorite_language #=> "Ruby" 27 / 51
Class Methods A method inted to be called on the class itself (not an instance) is called a class method Defined like an instance method, but starts with self. It cannot call any instance methods class Person def self.average_life_expectancy '71.5 years' p Person.average_life_expectancy #=> "71.5 years" 28 / 51
self Similar to the this keyword in Java There's always exactly one current object or self The current object is determined by the scope 29 / 51
Describing Methods Instance methods are conveyed with #: ClassName#instance_method (i.e. String#capitalize) Class methods are conveyed with ::: ClassName::class_method (i.e. Math::tan) Note that this has nothing to do with the way you define methods You will see this notation in Ruby Docs 30 / 51
Private Methods By default, all methods are public Of course you don't want to be able to access every method outside of the class Methods can be made private with the private keyword In the body of a class, all methods physically under private are private 31 / 51
Private Methods class Person def say_hello "Hi, I'm #{name}" private def name 'Sanj' person = Person.new p person.say_hello #=> "Hi, I'm Sanj" p person.name #=> NoMethodError: private method `name' called for #<Person 32 / 51
Class Variables Denoted with @@ at the beginning Similar to static variables in Java They are preserved across all instances of the class All instances can access and modify them 33 / 51
Class Variables class Person @@count = 0 def initialize @@count += 1 def self.count @@count p Person.count #=> 0 Person.new p Person.count #=> 1 They may seem harmless at first, but they're actually pretty bad practice 34 / 51
Inheritance Classes can inherit from only one other class Classes can be thought of as hierarchical Inheritance is done with the < operator A class will gain all of its parent class's methods, both public and private class Person class Programmer < Person p Programmer.superclass #=> Person 35 / 51
The Problem with Class Variables It turns out that class variables are more accurately described as "class hierarchy variables" Not only does the class have access to the variables, its descants will too This is pretty bad As a general rule, don't use class variables unless you mean for this to happen 36 / 51
The Problem with Class Variables class Bird @@location = 'EVERYWHERE' def self.location @@location p Bird.location #=> "EVERYWHERE" class Penguin < Bird @@location = 'Antarctica' def self.location @@location p Penguin.location #=> "Anarctica" p Bird.location #=> "Antarctica" 37 / 51
How do we work around this? Like I said earlier, almost everything in Ruby is an object All classes are instances of the Class class This means that classes are themselves objects class Person p Person.class #=> Class 38 / 51
Use Class Instance Variables We've already seen that objects can have instance variables Since classes are themselves objects, they too can have their own instance variables We will refer to a class's instance variables as class instance variables Define class instance variables within the class definition body OR within class methods with a single @ Remember that regular instance variables must be defined in an instance method or the constructor 39 / 51
Use Class Instance Variables Always use class instance variables, NEVER use class variables Work essentially like static variable in Java class Bird @location = 'EVERYWHERE' def self.location @location class Penguin < Bird @location = 'Antarctica' p Bird.location #=> "EVERYWHERE" p Penguin.location #=> "Antarctica" 40 / 51
Which is better style? # A class Person @@count = 0 # B class Person @count = 0 Vote at PollEv.com/CIS196776 41 / 51
Modules I mentioned earlier that classes can inherit from only one class This seems really limiting at first, but that's where modules come in Modules are bundles of methods and methods only Declared with the module keyword Unlike classes, modules cannot be instantiated 42 / 51
Modules module MyModule def my_module_method 'Hello World' MyModule.new #=> NoMethodError: undefined method `new' for My 43 / 51
Mixing in Modules Modules get mixed into classes using include or ext Referred to as "mix-ins" When you mix in a module into a class, that class gets access to all of the methods defined in that module 44 / 51
include vs. ext If you mix-in a module with: include: The module's methods will be available as instance methods ext: The module's methods will be available as class methods 45 / 51
Mixing in Modules module ClassMethodsModule def class_method 'Class Method' module InstanceMethodsModule def instance_method 'Instance Method' class MyClass ext ClassMethodsModule include InstanceMethodsModule p MyClass.class_method #=> "Class Method" p MyClass.new.instance_method #=> "Instance Method" 46 / 51
Separate Module Methods It should be clear if a method is meant to be an instance method or a class method It is extremely unlikely that a method is both Instance and class methods should be separated in the module with more, nested modules 47 / 51
Separate Module Methods module MyModule module InstanceMethods def instance_method p 'Instance Method' module ClassMethods def class_method p 'Class Method' 48 / 51
Using Nested Modules The :: operator can be used to access a module in another module Not to be confused with the documentation syntax of denoting a class method Thus, if a class wanted to access the module in the previous slide class MyClass include MyModule::InstanceMethods ext MyModule::ClassMethods MyClass.new.instance_method #=> "Instance Method" MyClass.class_method #=> "Class Method" 49 / 51
Requiring Files It's good practice for every class and module to have its own file To "import" other files, pass the path to the file as a string without the extension to the require method If the file foo.rb is in the same directory, put require './foo' at the top of the file If it's in the parent directory, require '../foo' Recall that requiring a file without the path imports a gem Note: "requiring" a file loads and runs that file 50 / 51
Homework 2 Homework 2 will be released after class Designed to help you become more comfortable with Object Orientation in Ruby You will be developing a workflow that is very similar to what you'll see when we get to Rails 51 / 51