Thursday, April 14, 2016

Ruby_Lesson_Object-Oriented Programming II

Information Hiding in Ruby
1.Need-to-Know Basis
Ruby allows you to explicitly make some methods public and others private. Public methods allow for an interface with the rest of the program; they say, "Hey! Ask me if you need to know something about my class or its instances."
Private methods, on the other hand, are for your classes to do their own work undisturbed. They don't want anyone asking them anything, so they make themselves unreachable!
3.Going Public
Methods are public by default in Ruby, so if you don't specify public or private, your methods will be public. In this case, however, we want to make it clear to people reading our code which methods are public. We do this by putting public before our method definitions, like so:
 class ClassName
  # Some class stuff
  public
  def public_method
    # public_method stuff
  end
end
Note that everything after the public keyword through the end of the class definition will now be public unless we say otherwise.For now, let's add a public method called bark to Dog. The bark method should puts "Woof!".
class Dog
    def initialize(name,breed)
        @name = name
        @breed = breed
    end
    public
    def bark
        puts "Woof!"
    end
end

4.Private! Keep Out!
Add a private method called id to Dog. The id method should create an @id_number instance variable and set it equal to 12345.
  private
    def id
        @id_number = 12345
    end

5.attr_reader, attr_writer











Ruby needs methods in order to access attributes. For instance, if we want to access a @name instance variable, we had to write something like
def name
  @name
end
Well, no longer! We can use attr_reader to access a variable and attr_writer to change it. If we write
class Person
  attr_reader :name
  attr_writer :name
  def initialize(name)
    @name = name
  end
end
Ruby does something like this for us automatically:
def name
  @name
end

def name=(value)
  @name = value
end
Like magic, we can read and write variables as we please! We just pass our instance variables (as symbols) to attr_reader or attr_writer.
(That name= might look funny, but you're allowed to put an = sign in a method name. That's just a Ruby convention saying, "hey, this method sets a value!")
class Person
  def initialize(name, job)
    @name = name
    @job = job
  end
 
  def name
    @name
  end
 
  def job=(new_job)
    @job = new_job
  end
end

We've brought back our Person class with a method for getting @name and a method for changing his or her @job. Go ahead and remove the name and job= methods and add an attr_reader for :name and an attr_writer for :job.
class Person
  def initialize(name, job)
    @name = name
    @job = job
  end
  attr_reader :name
  attr_writer :job
end

6.attr_accessor
If we want to both read and write a particular variable, there's an even shorter shortcut than using attr_reader and attr_writer. We can use attr_accessor to make a variable readable and writeable in one fell swoop.
Here we have an attr_reader and an attr_writer for :job. Go ahead and replace these with an attr_accessor!
attr_accessor :job
Introduction to Modules
7.What's a Module?
You can think of a module as a toolbox that contains a set methods and constants. There are lots and lots of Ruby tools you might want to use, but it would clutter the interpreter to keep them around all the time. For that reason, we keep a bunch of them in modules and only pull in those module toolboxes when we need the constants and methods inside!
You can think of modules as being very much like classes, only modules can't create instances and can't have subclasses. They're just used to store things!
module Circle
  PI = 3.141592653589793 
  def Circle.area(radius)
    PI * radius**2
  end 
  def Circle.circumference(radius)
    2 * PI * radius
  end
end

8.Module Syntax
module ModuleName
  # Bits 'n pieces
end
Like class names, module names are written in CapitalizedCamelCase, rather than lowercasewithunderscores.
It doesn't make sense to include variables in modules, since variables (by definition) change (or vary). Constants, however, are supposed to always stay the same, so including helpful constants in modules is a great idea.
Ruby doesn't make you keep the same value for a constant once it's initialized, but it will warn you if you try to change it. Ruby constants are written in ALL_CAPS and are separated with underscores if there's more than one word.
An example of a Ruby constant is PI, which lives in the Math module and is approximately equal to 3.141592653589793.
Create your own module called MyLibrary in the editor to the right. Include a constant called FAVE_BOOK and set it equal to a string naming your favorite book!
module MyLibrary
    FAVE_BOOK = "Harry Potter"
end

9.Resolve to Keep Learning
One of the main purposes of modules is to separate methods and constants into named spaces. This is called (conveniently enough) namespacing, and it's how Ruby doesn't confuse Math::PI and Circle::PI.
See that double colon we just used? That's called the scope resolution operator, which is a fancy way of saying it tells Ruby where you're looking for a specific bit of code. If we say Math::PI, Ruby knows to look inside the Math module to get that PI, not any other PI (such as the one we created in Circle).
Use the scope resolution operator to puts the value of PI from the Math module to the console.
puts Math::PI
10.A Few Requirements
Some modules, like Math, are already present in the interpreter. Others need to be explicitly brought in, however, and we can do this using require. We can do this simply by typing
require 'module'
We want to use the Ruby Date module to show today's date, but we haven't required it yet!
require 'date'
puts Date.today

11.Feeling Included
We can do more than just require a module, however. We can also include it!
Any class that includes a certain module can use those module's methods!
A nice effect of this is that you no longer have to prepend your constants and methods with the module name. Since everything has been pulled in, you can simply write PI instead of Math::PI.
class Angle
    include Math 
  attr_accessor :radians
  def initialize(radians)
    @radians = radians
  end 
  def cosine
    cos(@radians)
  end
end
acute = Angle.new(1)
acute.cosine

Fixin' for a Mixin
12.The Marriage of Modules and Classes
When a module is used to mix additional behavior and information into a class, it's called a mixin. Mixins allow us to customize a class without having to rewrite code!
module Action
  def jump
    @distance = rand(4) + 2
    puts "I jumped forward #{@distance} feet!"
  end
end

class Rabbit
  include Action
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

class Cricket
  include Action
  attr_reader :name
  def initialize(name)
    @name = name
  end
end

peter = Rabbit.new("Peter")
jiminy = Cricket.new("Jiminy")

peter.jump
jiminy.jump

13.Imitating Multiple Inheritance
Now you understand why we said mixins could give us the ability to mimic inheriting from more than one class: by mixing in traits from various modules as needed, we can add any combination of behaviors to our classes we like!
14.Extend Your Knowledge
Whereas include mixes a module's methods in at the instance level (allowing instances of a particular class to use the methods), the extend keyword mixes a module's methods at the class level. This means that class itself can use the methods, as opposed to instances of the class.
# ThePresent has a .now method that we'll extend to TheHereAnd

module ThePresent
  def now
    puts "It's #{Time.new.hour > 12 ? Time.new.hour - 12 : Time.new.hour}:#{Time.new.min} #{Time.new.hour > 12 ? 'PM' : 'AM'} (GMT)."
  end
end

class TheHereAnd
  extend ThePresent
end

TheHereAnd.now

Project
1.What You'll Be Building
In this case, we'll be making an Account object with public methods to display balances and transfer funds, but which rely on private methods to make sure the user's PIN (personal identification number) is correct before approving transactions.
2.Creating the Account Class
def initialize(name, balance=100)
  @name = name
  @balance = balance
What's that balance=100 doing? It's signifying an optional parameter. Ruby is saying that you can pass one or two arguments to initialize; if you pass two, it uses your balance argument to set @balance; if you only pass a name, balance gets a default value of 100, and that's what gets stored in @balance.
You probably also noticed we used underscores in our 1_000_000 (one million). Ruby allows this, and it makes it easier to read big numbers!Create an Account class in the editor. The :name and :balance attributes should be readable (but not writeable!). The class' initialize method should take two parameters, name and balance, and the balance parameter should default to 100.
Finally, go ahead and store your parameters in instance variables @name and @balance, respectively.
class Account
    attr_reader :name
    attr_reader :balance
    def initialize(name,balance=100)
        @name = name
        @balance = balance
    end
end

3.Private Affairs
Add two private methods to your Account class, pin and pin_error.
pin should take no arguments and set an instance variable, @pin, equal to the PIN number 1234.
pin_error should take no arguments and should simply return the string "Access denied: incorrect PIN."
class Account
    attr_reader :name
    attr_reader :balance
    def initialize(name,balance=100)
        @name = name
        @balance = balance
    end
    private
    def pin
        @pin = 1234
    end
    private
    def pin_error
        return "Access denied: incorrect PIN."
    end
end

4.Displaying the Balance
  1. Define a public display_balance method to your Account class. It should take a single parameter, pin_number.
  2. The body of your method should check whether the pin_number is equal to pin (the result of calling the private pin method)
  3. If it is, display_balance should puts "Balance: $#{@balance}."
  4. Otherwise (else), it should puts pin_error (the pin_error message)
 public
    def display_balance(pin_number)
        if pin_number == pin.call
            puts "Balance: $#{@balance}."
        else
            puts pin_error
        end
    end

5.Making a Withdrawal
Add a public withdraw method to your class that takes two parameters, pin_number and amount. If pin_number matches pin, your method should subtract the amount from the balance and puts "Withdrew #{amount}. New balance: $#{@balance}." Otherwise, it should puts pin_error.
public
    def withdraw(pin_number,amount)
        if pin_number == pin
            @balance -= amount
            puts "Withdrew #{amount}. New balance: $#{@balance}."
        else
            puts pin_error
        end
    end

6.Opening an Account
Last step: create an instance of your Account class called checking_account. Give it whatever name and starting balance you like!
 checking_account = Account.new("Becky",123)













4 comments :