class BookInStock
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
end
a_book = BookInStock.new
another_book = BookInStock.new
initialize method is a special method in Ruby programs. When you call BookInStock.new to create a new object, Ruby allocates some memory to hold an uninitialized object and then calls that object's initialize method, passing in any parameters that were passed to new.
puts method simply writes strings to your program's standard output. When you pass it an object based on a class you wrote, it doesn't really know what to do with it, so it uses a very simple expedient: it writes the name of the object's class, followed by a colon and the object's unique identifier (a hexadecimal number). It puts the whole lot inside #<...>.
一、Objects and Attributes
1.Ruby provides a convenient shortcut. attr_reader creates these attribute reader methods for you:
class BookInStock
attr_reader :isbn, :price
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
# ..
end
book= BookInStock.new("isbn1", 12.34)
puts "ISBN =#{book.isbn}"
puts "Price =#{book.price}"
produces:
ISBN = isbn1
Price = 12.34
This is the first time we've used symbols in this chapter.symbols are just a convenient way of referencing a name.
(1). Writable Attributes
class BookInStock
attr_reader :isbn, :price
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
def price=(new_price)
@price = new_price
end
# ...
end
book = BookInStock.new("isbn1", 33.80)
puts "ISBN =#{book.isbn}"
puts "Price =#{book.price}"
book.price = book.price * 0.75 # discount price
puts "New price =#{book.price}"
produces:
ISBN = isbn1
Price = 33.8
New price = 25.349999999999998
If you create a method whose name
ends
with an equals sign, that name can appear on the left side of an assignment.
Ruby provides a shortcut for creating these simple attribute-setting methods. If you want a write-only accessor, you can use the form attr_writer, but that's fairly rare.
You're far more likely to want both a reader and a writer for a given attribute, so you'll use the handydandy attr_accessor method:
class BookInStock
attr_reader :isbn
attr_accessor :price
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
# ...
end
book = BookInStock.new("isbn1", 33.80)
puts "ISBN =#{book.isbn}"
puts "Price =#{book.price}"
book.price = book.price * 0.75 # discount price
puts "New price =#{book.price}"
produces:
ISBN = isbn1
Price = 33.8
New price = 25.349999999999998
(2)Virtual Attributes 虚拟属性 => [Continued]
class BookInStock
attr_reader :isbn
attr_accessor :price
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
def price_in_cents
Integer(price*100 + 0.5)
end
def price_in_cents=(cents)
@price = cents / 100.0
end
# ...
end
book = BookInStock.new("isbn1", 33.80)
puts "Price = #{book.price}"
puts "Price in cents = #{book.price_in_cents}"
book.price_in_cents = 1234
puts "Price =#{book.price}"
puts "Price in cents =#{book.price_in_cents}"
produces:
Price = 33.8
Price in cents = 3380
Price = 12.34
Price in cents = 1234
(3)Attributes, Instance Variables, and Methods
When you design a class, you decide what internal state it has and also decide how that state is to appear on the outside (to users of your class). The internal state is held in instance variables. The external state is exposed through methods we're calling attributes. And the other actions your class can perform are just regular methods. It really isn't a crucially important distinction, but by calling the external state of an object its attributes, you're helping clue people in to how they should view the class you've written.
2.Classes Working with Other Classes
book_in_stock.rb
class BookInStock
attr_reader :isbn, :price
def initialize(isbn, price)
@isbn = isbn
@price = Float(price)
end
end
csv_reader.rb
require 'csv'
require_relative 'book_in_stock'
class CsvReader
def initialize
@books_in_stock = []
end
def read_in_csv_data(csv_file_name)
CSV.foreach(csv_file_name, headers: true) do |row|
@books_in_stock << BookInStock.new(row["ISBN"], row["Price"])
end
end
def total_value_in_stock # later we'll see how to use inject to sum a collection
sum = 0.0
@books_in_stock.each {|book| sum += book.price}
sum
end
def number_of_each_isbn
# ...
end
end
stock_stats.rb
require_relative 'csv_reader'
reader = CsvReader.new
ARGV.each do |csv_file_name|
STDERR.puts "Processing #{csv_file_name}"
reader.read_in_csv_data(csv_file_name)
end
puts "Total value = #{reader.total_value_in_stock}"
It uses the ARGV variable to access the program's command-line arguments, loading CSV data for each file specified on the command line.
3.Access Control
The good news is that the only easy way to change an object's state in Ruby is by calling one of its methods.
Control access to the methods, and you've controlled access to the object.
Ruby gives you three levels of protection:
Public methods can be called by anyone—no access control is enforced. Methods are public by default (except for initialize, which is always private).
Protected methods can be invoked only by objects of the defining class and its subclasses. Access is kept within the family.
Private methods cannot be called with an explicit receiver
This means that private methods can be called only in the context of the current object; you can't invoke another object's private methods.
If a method is protected, it may be called by any instance of the defining class or its subclasses.
If a method is private, it may be called only within the context of the calling object—it is never possible to access another object's private methods directly, even if the object is of the same class as the caller.
(1)Specifying Access Control
class MyClass
def method1 # default is 'public'
#...
end
protected # subsequent methods will be 'protected'
def method2 # will be 'protected'
#...
end
private # subsequent methods will be 'private'
def method3 # will be 'private'
#...
end
public # subsequent methods will be 'public'
def method4 # so this will be 'public'
#...
end
end
Alternatively, you can set access levels of named methods by listing them as arguments to the access control functions:
class MyClass
def method1
end
def method2
end
# ... and so on
public :method1, :method4
protected :method2
private :method3
end
some examples:
class Account
attr_accessor :balance
def initialize(balance)
@balance = balance
end
end
class Transaction
def initialize(account_a, account_b)
@account_a = account_a
@account_b = account_b
end
private
def debit(account, amount)
account.balance -= amount
end
def credit(account, amount)
account.balance += amount
end
public
#...
def transfer(amount)
debit(@account_a, amount)
credit(@account_b, amount)
end
#...
end
savings = Account.new(100)
checking = Account.new(200)
trans = Transaction.new(checking, savings)
trans.transfer(50)
4.Variables
Variables are used to keep track of objects; each variable holds a reference to an object.
So, is a variable an object? In Ruby, the answer is "no". A variable is simply a reference to an object.
Objects float around in a big pool somewhere (the heap, most of the time) and are pointed to by variables.
you could avoid aliasing by using the dup method of String, which creates a new string object with identical contents.
You can also prevent anyone from changing a particular object by freezing it. Attempt to alter a frozen object, and Ruby will raise a Runtime Error exception:
person1 ="Tim"
person2 = person1
person1.freeze # prevent modifications to the object
person2[0] ="J"
produces:
from prog.rb:4:in `<main>'
prog.rb:4:in `[]=': can't modify frozen String (RuntimeError)
for now, know that everything you manipulate in Ruby is an object and that objects start life as instances of classes. And one of the most common things we do with objects is
create collections of them.