Ruby 组织对象用的类的用法介绍

类与实例

2016年9月5日 下午7:35 ***

一个类里面会定义一些方法,类存在的理由就是要被实例化,也就是去创建一个类的实例得到一个对象。一个实例化的动作,像这样:

obj = Object.new
Object 是 Ruby 内置的一个类,在类上使用点形式,就是 Object 与 new 之间的那个点。你就是发送了一个信息给类。类会对这个信息做出响应,就像对象可以响应信息一样。类也是对象。new 方法是一个构造器,也就是类里面的可以加工与返回新实例的方法。

使用 class 关键词可以去定义类,类的名字用的是常量,使用大写字母开头。常量可以存储信息,常量的值是可以改变的,但是你不能为常量去分配一个新的值,这样 Ruby 会警告你。最好的方法是避免为已经有值的常量分配新的值。

来创建一个 Ticket 类,它里面有一个简单的方法:

class Ticket
  def event
    'can not really be specified yet...'
  end
end
现在可以创建一个新的 ticket 对象,然后问一下它的 event:

ticket = Ticket.new
puts ticket.event
调用方法 ticket.event ,会执行 event 方法,也就会输出这个方法里要输出的东西:

can not really be specified yet...
在上面的例子里我们创建与执行了一个实例方法。

下午7:47 ****

实例方法

之前我们直接在一个对象的上面定义了一个方法,是这样做的:

def ticket.event
刚才我们在 Ticket 类里又定义一下 event 这个方法:

def event
这种在类里定义的方法会出现在所有的这个类的实例上,这种方法就是实例方法(instance methods)。它们不会只属于一个对象,所有的实例都可以调用它们。

我们在某个特定的对象上面定义的方法叫做独立方法(singleton methods),比如像 def ticket.price 。一个对象有一个 price 方法,这个对象不会在乎这个方法到底是一个独立方法还是一个实例方法,不过作为程序员我们应该知道它们的区别。

下午7:54 ****

覆盖方法

下午7:54 ****

在类里面定义了方法,我们可以重新再定义它,在下面的例子里重复定义了两次 m 这个方法:

class C
  def m
    puts '第一次定义方法 m'
  end

  def m
    puts '第二次定义方法 m'
  end
end
来看一下我们在 C 的一个实例上调用 m 这个方法会发生什么:

C.new.m
输出的结果是 “第二次定义方法 m ”,第二次的定义胜利了,因为我们看到的是第二次定义 m 这个方法的时候要输出的东西。当我们覆盖一个方法的时候,新的版本会胜利。

下午8:02 ***

重新打开类

下午8:02 ***

大部分情况下,定义一个类,你就创建了一个类定义的区块:

class C
  # 这里是类的代码
end
重新再打开这个类添加额外的东西或者修改是可以做到的。像这样:

class C
  def x
  end
end

class C
  def y
  end
end
我们打开了类的定义主体,添加了一个 x 方法,关掉了定义主体。然后,我们又重新打开了定义主体,添加了第二个方法 y ,又关掉了定义主体。跟下面这么做是一样的效果:

class C
  def x
  end

  def y
  end
end
下午8:07 ****

实例变量与对象状态

下午8:09 ****

跟一个对象相关的信息与数据是这个对象的状态(state)。我们需要做到:

设置与重置一个对象的状态(一张票说,你花了 10 块钱)
读回状态(问一张票,你需要花多少钱?)
实例变量(instance variables),就是 Ruby 对象使用的存储与取回值的机制。实例变量让每个独立的对象可以记住它们的状态。实例变量跟其它的变量差不多,你分配值给它们,你可以读回这些值,你可以把它们加到一块儿,输出它们等等。不过有几个不一样的地方:

实例变量的名字要用 @ 符号开头,这样你很容易知道哪些是实例变量。
实例变量只能在它们所属的那个对象的内部使用。
在类的内部的某个方法里面初始化一个实例变量,这个实例变量就可以在类的任何方法里面使用。
来看个例子:

class Person
  def set_name(string)
    puts '设置人名...'
    @name = string
  end

  def get_name
    puts '返回人名...'
    @name
  end
end

joe = Person.new
joe.set_name('Joe')
puts joe.get_name
调用 set_name 方法的时候我们在这个方法里分配了一个实例变量,名字是 @name,这个变量也可以在类里的其它的方法上使用,比如我们在 get_name 这个方法里就用了一个 @name 这个实例变量。

下午8:25 ****

初始化带状态的对象

下午8:27 ****

在类里面可以定义一个名字是 initialize 的方法,每次创建新的实例的时候,都会自动调用这个方法。来看个例子:

class Ticket
  def initialize
    puts '创建新票'
  end
end
调用 Ticket.new 的时候,你就会看到一个 “创建新票” 的信息出现。

你可以利用这个自动初始化的方法,在创建对象的时候去设置对象里的状态。比如你想在创建票的时候让票拥有会场(venue)与日期(date)这两个状态,你可以在创建实例的时候让它们作为参数的值,这些参数的值也会传递给 initialize 方法,这样你就可以把这些参数的值保存成实例变量:

class Ticket
  def initialize(venue,date)
    @venue = venue
    @date = date
  end
关闭定义类的区块之前,再做点别的事,就是添加一种可以读回 venue 与 date 值的方法。继续再添加下面这段代码:

  def venue
    @venue
  end
 
  def date
    @date
  end
end
这两个方法里面用到了实例变量,而且它们都是方法里的最后一个表达式,也就是它们会作为方法返回的值。

下午8:40 ****

设置器方法

下午8:51 ***

名字里带等号的方法

class Ticket
  def initialize(venue,date,price)
    @venue = venue
    @date = date
    @price = price
  end

  def price
    @price
  end
end

初始化的那个方法会变得很长,而且我们还得记住参数的顺序。可以改进一下,比如我们用一个 set_price 方法,可以用来设置或重置票的价格,这样再改一下:

class Ticket
  def initialize(venue,date,price)
    @venue = venue
    @date = date
  end

  def set_price(amount)
    @price = amount
  end

  def price
    @price
  end
end
上面的 set_price 也可以写成这样:

def price=(amount)
  @price = amount
end
方法是用 = 号结尾的,使用它的时候可以这样:

ticket.price=(63.00)
或者直接这样用:

ticket.price = 63.00
上面其实是一个方法的调用,不过它看起来像是一个分配的表达式。这种形式是 Syntactic sugar,它表示的是一种使用特别规则的写法。

下午9:32 ****

属性与 attr* 方法家族

2016年9月6日 上午7:38 ***

attribute 与 property 翻译成中文都可以是 “属性”。属性的值可以通过对象读与写。比如之前我们见过的那个 票 对象,每张票都有 price,date,venue 属性(attribute)。price= 这个方法可以看成是属性的写的方法,date,venue,还有 price 这些读取属性用的方法。write/read 跟 get/set 是一个意思。

自动创建属性

再看一下之前的这段代码:

class Ticket
  def initialize(venue, date)
    @venue = venue
    @date = date
  end

  def price=(price)
    @price = price
  end

  def venue
    @venue
  end

  def date
    @date
  end

  def price
    @price
  end
end
从属性的读写方面来看,上面有一个读/写属性(price),还有两个读属性(venue 和 date)。像上面这么干也可以,不过就是有点重复,比如这三个方法的形式都是这样的:

def something
  @something
end
Ruby 提供了自动创建读取与返回实例变量值的方法,像这样:

class Ticket
  attr_reader :venue, :date, :price
end
上面有几个东西是用冒号开头的,这些是符号(symbols),符号是命名或打标记用的东西。以后再具体解释它跟字符串的区别。

self 是默认的接受者

你看到了一些调用的方法没有明显的接受者,比如调用 attr_reader 的时候。在这种情况下,信息会发送给 self ,它是默认的对象。在定义类的主体的顶级,self 是类对象本身,也就是接受 attr_reader 信息的其实是 Ticket 这个类对象。

还有个 attr_writer 方法,像这样用:

class Ticket
  attr_writer :price
end
再用这两个 attr_* 方法改进一下 Ticket 类:

class Ticket
  attr_reader :venue, :date, :price
  attr_writer :price
 
  def initialize(venue, date)
    @venue = venue
    @date = date
  end
end
这样一个 ticket 对象会有 venue,date,price 这几个属性,venue,date 是可读属性,price 是可读也可写的属性。

941E615E-5ECF-4567-8217-DDD888F33DE5

用 attr_accessor 创建读写属性

使用 attr_accessor 可以为属性创建读与写的方法。它相当于是 attr_reader 加上 attr_writer 。

class Ticket
  attr_reader :venue, :date
  attr_accessor :price
  # ... etc.
end
还有个 attr ,这样用:

attr :price, true
第二个参数 true 表示使用读与写的方法。

attr*  方法的总结

attr_reader

代码:
attr_reader :price

相当于:
def price
  @price
end
attr_writer

代码:
attr_writer :price

相当于:
def price=(price)
  @price = price
end
attr_accessor

代码:
attr_accessor :price

相当于:
def price=(price)
  @price = price
end

def price
  @price
end
attr

代码:
# 相当于 attr_reader
attr :price

# 相当于 attr_accessor
attr :price, true
上午8:26 ***

继承

上午9:00 ***

继承是两个类之间的一种关系,一个类从另一个类里继承了一些行为(subclass,superclass)。看一个例子,Magazine 继承了 Publication ,Magazine 是 Publication 类的 subclass,Publication 是 Magazine 类的 superclass:

class Publication
  attr_accessor :publisher
end

class Magazine < Publication
  attr_accessor :editor
end
Magzine 继承的时候用了一个 < 符号,它是一个 subclass,它继承的是 Publication 这个类,这个类对于 Magzine 这个类来说是一个 superclass。

做个实验:

mag = Magazine.new
mag.publisher = 'ninghao.net'
mag.editor = '张三'
puts '#{mag.publisher} 是杂志的发行者,#{mag.editor} 是杂志的编辑'
因为 Magzine 类继承了 Publication 类,所以 Magzine 的对象里面也会包含 Publication 里面的属性与方法。

我们可以继续:

class Ezine < Magzine
end
这样 Ezine 的对象里面就会有 publisher 和 editor 这两个属性。每个 Ruby 类只能继承一个类。

做个实验:

class C
end

class D < C
end

puts D.superclass
puts D.superclass.superclass
输出的是:

C
Object
C 是 D 的 superclass,Object 是 C 的 superclass 。

上午9:12 ***

作为对象与信息接受者的类

上午9:12 ***

类是一种特别的对象,它是唯一的能生对象的对象。创建了一个类,比如 Ticket ,你可以发送信息给它,添加方法给它,把它作为方法的参数传递给其它的对象,你可以像处理其它的对象一样处理类对象。

创建类对象

所有的者,比如 Object,Person,Ticket 都是 Class 这个类的实例。使用 class 关键词创建一个类对象:

class Ticket
  # 代码
end
使用上面这种方法创建类对象非常容易读懂,你也可以这样来创建类对象:

my_class = Class.new
上面创建的这个类对象可以创建它自己的实例:

instance_of_my_class = my_class.new
如果你想使用 Class.new 去创建一个匿名的类,而且想在创建它的时候给它添加实例方法,可以在 Class.new 的后面添加一个代码块(code block),像这样:

c = Class.new do
  def say_hello
    puts 'hello'
  end
end
上午10:17 ****

Ruby 对象的天性与培育

is_a? 方法可以告诉你一个实例是否属于某个类,或者它所属类的祖先类:

>> mag = Magazine.new
=> #
>> mag.is_a?(Magazine)
=> true
>> mag.is_a?(Publication)
=> true
一个实例不完全受它所属的类的影响,你可以为单独的实例对象添加方法,比如让 magzine 长出翅膀:

mag = Magazine.new
def mag.wings
  puts 'I can fly!'
end
mag.wings
常量

上午10:17 ****

常量的名字是大写字母开头的。类的名字是常量,常量也经常会用在类里面来保存重要的数据。

一个例子:

class Ticket
  VENUES = ["Convention Center", "Fairgrounds", "Town Hall"]
如果你想让每张票都来自 VENUES 里定义的场地,可以在类的 initialize 方法里这样做:

def initialize(venue, date)
  if VENUES.include?(venue)
    @venue = venue
  else
    raise ArgumentError, "Unknown venue #{venue}"
  end
  @date = date
end
在定义类的外面可以使用双冒号得到常量的值:

class Ticket
  VENUES = ["Convention Center", "Fairgrounds", "Town Hall"]
end

puts Ticket::VENUES
Ruby 的预定义常量

>> Math::PI
=> 3.141592653589793
>> RUBY_VERSION
=> "2.3.1"
>> RUBY_PATCHLEVEL
=> 112
>> RUBY_RELEASE_DATE
=> "2016-04-26"
>> RUBY_REVISION
=> 54768
>> RUBY_COPYRIGHT
=> "ruby - Copyright (C) 1993-2016 Yukihiro Matsumoto"
>>
重新分配与修改常量

给一个已经分配了值的常量重新分配值,会收到 Ruby 的警告,试一下:

>> A = 1
=> 1
>> A = 2
(irb):36: warning: already initialized constant A
(irb):35: warning: previous definition of A was here
=> 2
不过你可以修改常量的值:
venues = Ticket:VENUES
venues << '体育馆'

时间: 2024-09-24 17:50:44

Ruby 组织对象用的类的用法介绍的相关文章

[ASP.NET AJAX]Function对象及Type类的方法介绍

ajax|asp.net|对象 上一回我们从总体上认识了JavaScript Microsoft AJAX Library由于临时有事,没有加入事例显得有点抽象,这一回一定会通过一些事例更加直观的来会一会Asp.NET Ajax脚本库中一个很是重要的类Type.这个类提供了一些扩展面向对象编程的一些反射方法,通过这个类我们可以注册类似.NET中的一些(如:命名空间,类,枚举等等)基本类型.这个Type类继承自window是一个Global类型,不属于任何命名空间.下面我们来看看Type中的一些基

[ASP.NET AJAX]Function对象及Type类的方法介绍_AJAX相关

上一回我们从总体上认识了JavaScript Microsoft AJAX Library由于临时有事,没有加入事例显得有点抽象,这一回一定会通过一些事例更加直观的来会一会Asp.NET Ajax脚本库中一个很是重要的类Type.这个类提供了一些扩展面向对象编程的一些反射方法,通过这个类我们可以注册类似.NET中的一些(如:命名空间,类,枚举等等)基本类型.这个Type类继承自window是一个Global类型,不属于任何命名空间.下面我们来看看Type中的一些基本方法以及是怎样实一些方法的实现

Python类的用法介绍

第一形式 # !/usr/bin/env python # coding=utf-8 class Person(object): #object表示继承自object类,Python3中可省略次内容     """     This is a sample of Class     """     breast = 90  #类的属性 是静态变量         def __init__(self, name): #初始化方法  self为对象实

php excel操作类phpExcel用法介绍

下面是总结的几个使用方法 include 'PHPExcel.php'; include 'PHPExcel/Writer/Excel2007.php'; //或者include 'PHPExcel/Writer/Excel5.php'; 用于输出.xls的 创建一个excel $objPHPExcel = new PHPExcel(); 保存excel-2007格式 $objWriter = new PHPExcel_Writer_Excel2007($objPHPExcel); //或者$o

在Ruby中利用Net::SMTP类发送电子邮件的教程

  这篇文章主要介绍了在Ruby中利用Net::SMTP类发送电子邮件的教程,包括类中所带方法的用法介绍,需要的朋友可以参考下 简单邮件传输协议(SMTP)发送电子邮件及路由的e-mail邮件服务器之间的协议处理. Ruby 提供 Net::SMTP 类的简单邮件传输协议(SMTP)客户端的连接,并提供了两个新的方法:new 和 start. new 带两个参数: server name 默认为 localhost port number 默认为熟知的 25 start 方法带有以下这些参数:

java object用法-Java中object类的用法

问题描述 Java中object类的用法 class Student { String name; int age; public boolean equals (Object obj) { Student st = null; if(obj instanceof Student) st =(Student)obj; else return false; if(st.name==this.name&&st.age==this.age) return true; else return fa

JS扩展类,克隆对象与混合类实例分析_javascript技巧

本文实例讲述了JS扩展类,克隆对象与混合类.分享给大家供大家参考,具体如下: 1.类扩展 /* EditInPlaceField类 */ /* 扩展函数 */ function extend(subClass, superClass) { var F = function() {}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass

学习Java中Class类及其用法_java

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI.这项信息纪录了每个对象所属的类.虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类.Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建. 简单总结如下: Class类也是类的一种,只是名字和class关键字高度相似.Java是大小写敏感的语言. Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,J

Android中异步类AsyncTask用法总结_Android

本文总结分析了Android中异步类AsyncTask用法.分享给大家供大家参考,具体如下: 最近整理笔记的时候,看到有关AsyncTask不是很理解,重新疏导了一下,有在网上找了一些资料,个人不敢独享,一并发在这里与大家共勉 这里有两种解释的方法,各有侧重点: 第一种解释: Async Task 简介: AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦 AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Pa