Object oriented programming (OOP) is a programming paradigm. It allows us to represent the complexities of the real, physical world in a digital way. Languages that support some level of object-orientation are ubiquitous — they include Ruby, Python, Javascript, PHP, C++, C #, Java, Rust, Kotlin, Go, and many others.
面向对象编程(OOP)是一种编程范例。 它使我们能够以数字方式表示现实世界的复杂性。 支持某种程度的面向对象的语言无处不在-它们包括Ruby,Python,Javascript,PHP,C ++,C#,Java,Rust,Kotlin,Go以及许多其他语言。
This is the first article in a two-part series that will approach the topic of OOP using Ruby to illustrate ideas and is intended for those who have an understanding of the fundamentals of procedural programming and are learning OOP.
这是一个由两部分组成的系列文章的第一篇,该系列文章将使用Ruby来探讨OOP主题,以说明思想,并且适合那些了解过程编程基础并且正在学习OOP的人。
At its core, OOP consists of classes and objects made from those classes.
OOP的核心是类和由这些类组成的对象。
类决定属性和行为 (Classes determine attributes and behaviors)
A class is a blueprint or a template for objects. It is the concept or idea of a thing, while an object is one physical manifestation of that concept. For example, take the concept of a book. Picture one in your mind. What do you see? In other words, what makes a book a book?
类是对象的蓝图或模板。 它是事物的概念或观念 ,而对象是该概念的一种物理表现。 例如,以一本书的概念为例。 想一想。 你看到了什么? 换句话说,是什么使一本书成为一本书?
A book has attributes
一本书具有属性
- Some potentially captivating title 一些潜在的迷人标题
- Some author/s who wrote it 一些作者写的
- Some number of pages 若干页
- Some type of paperback or hardcover binding 某种平装书或精装本
- A cover that is some color 有颜色的封面
- Some measurable weight 一些可衡量的体重
These are all characteristics — they describe the book. Instance variables are the attributes that an individual book has (e.g., a title of Harry Potter and the Chamber of Secrets, a page number of 432, a weight of 0.74 lbs). The total amalgamation of all instance variables an individual book has make up that particular book’s state.
这些都是特征-它们描述了本书。 实例变量是一本书所具有的属性(例如,《 哈利·波特与密室》的书名,页数为432,权重为0.74磅)。 一本书的所有实例变量的总合并构成了该书的状态。
Specific actions can be done with a book
一本书可以完成特定的动作
- It can be read 可以阅读
- It can be picked up 可以拿起
- It can be put down 可以放下
- It can be thrown 可以扔
- Pages can be ripped out 页面可以被撕掉
- Notes can be written in the margins 笔记可以写在空白处
In OOP, the actions that you can do with an object are defined in the class definition. They are called instance methods, and can be called on any object made from the class.
在OOP中,可以在类定义中定义可以对对象执行的操作。 它们被称为实例方法 ,并且可以在由类制成的任何对象上被调用。
Taking a step back, a class dictates the possible attributes or characteristics of objects that are made from it.
退后一步,类规定了由此类对象的可能属性或特性。
Let’s focus on three key attributes — the title, author, and number of pages to create a Book
class below.
让我们关注三个关键属性-标题,作者和在下面创建Book
类的页面数。
class Book
def initialize(title, author, pages)
@title = title
@author = author
@pages = pages
end
end
Within our simple Book
class definition is a special instance method initialize
. This is Ruby’s constructor method. It is triggered every time the class method ::new
is called on the Book
class. Note that initialize
is a private instance method, which means it cannot be called outside of a class definition. Additionally, if the initialize method is defined to take parameters, the ::new
method must be passed arguments (unless the parameters defined within the class definition are given default parameters).
在我们简单的Book
类定义中,有一个特殊的实例方法initialize
。 这是Ruby的构造方法。 每次在Book
类上调用类方法::new
时,都会触发该事件。 请注意, initialize
是一个私有实例方法,这意味着不能在类定义之外调用它。 此外,如果将initialize方法定义为采用参数,则必须将::new
方法传递给参数(除非在类定义中定义的参数被赋予了默认参数)。
实例变量组成状态 (Instance variables compose state)
You may have noticed the variables prefixed with @
. Instance variables are denoted with the @
sign (also known as the sigil). These variables are scoped at the object level — they are available to all instance methods defined within a class, and each object made from a class has its own copy of the variable that can hold its own value.
您可能已经注意到以@
开头的变量。 实例变量用@
符号表示(也称为符号)。 这些变量在对象级别作用域-它们可用于类中定义的所有实例方法,并且由类构成的每个对象都有其自己的变量副本,这些副本可以保存自己的值。
With our class defined, we can call new
directly on the class, feeding it the information we want to use to create the initial state of the object.
定义好我们的类后,我们可以直接在类上调用new
,向它提供我们想要用来创建对象初始状态的信息。
Book.new('Triumph of the City', 'David Glaeser', 338)
We have created, or instantiated one Book object — one instance of the Book
class. This book
has a bundle of instance variables — @title
, @author
, and @pages
. The instance variables of this particular instance of the class are bound to the values we pass to the ::new
method invocation. In other words, this Book
object has a unique state that is made up of all the objects bound to its instance variables.
我们已经创建或实例化了一个Book对象Book
类的一个实例 。 这book
有实例变量的束- @title
, @author
和@pages
。 该类的特定实例的实例变量绑定到我们传递给::new
方法调用的值。 换句话说,此Book
对象具有唯一状态,该状态由绑定到其实例变量的所有对象组成。
We can also assign this object to a local variable for safekeeping.
我们还可以将此对象分配给本地变量以进行保管。
triumph_of_the_city = Book.new('Triumph of the City', 'David Glaeser', 338)
How can we access data stored within an object? How can we find the number of pages in our Book
object? Right now — our options are limited. If you’re interested in gaining a deeper knowledge of Ruby’s meta-programming, look into Object#instance_variables
and Object#instance_variable_get
.
我们如何访问存储在对象中的数据? 我们如何在Book
对象中找到页数? 目前-我们的选择是有限的。 如果您想对Ruby的元编程有更深入的了解,请查看Object#instance_variables
和Object#instance_variable_get
。
As a general rule of thumb, we cannot directly access an object’s instance variables outside of the class definition — they are private and only accessible within the scope of the object. However, we can define instance methods within the class definition and reference instance variables there. These methods can then be called on an object of that class to access and interact with that object’s instance variables.
根据一般经验,我们不能在类定义之外直接访问对象的实例变量-它们是私有的,只能在对象范围内访问。 但是,我们可以在类定义中定义实例方法,并在那里引用实例变量。 然后可以在该类的对象上调用这些方法,以访问该对象的实例变量并与之交互。
class Book
def initialize(title, author, pages)
@title = title
@author = author
@pages = pages
enddef title
@title
end
end
Here, we’ve added an instance method title
, in which we access the instance variable @title
. Because this method is available to all Book
objects, we can call title
like so.
在这里,我们添加了一个实例方法title
,在其中访问实例变量@title
。 因为此方法可用于所有Book
对象,所以我们可以像这样调用title
。
triumph_of_the_city.title
This returns the title of the book.
这将返回书名。
Instance methods that access instance variables are called getter methods, and those that allow a person to modify instance variables are called setter methods. Let’s write a few more getters and a setter method.
访问实例变量的实例方法称为getter方法,而允许人修改实例变量的实例方法称为setter方法。 让我们再写一些getter和setter方法。
class Book
def initialize(title, author, pages)
@title = title
@author = author
@pages = pages
enddef title
@title
enddef author
@author
enddef pages
@pages
enddef pages=(new_pages)
@pages = new_pages
end
end
As you can see, we now have getter methods for title
, author
, and pages
. We also have a setter method for pages
. This one might seem strange. It’s a Ruby convention to use =
at the end of the name of a setter method. It’s similar to predicate methods ending with ?
.
如您所见,我们现在具有用于title
, author
和pages
getter方法。 我们还有用于pages
的setter方法。 这可能看起来很奇怪。 在setter方法名称的末尾使用=
是一种Ruby约定。 它类似于以?
结尾的谓词方法?
。
The setter takes one argument, the new value that the user wants to assign to the instance variable. With Ruby’s syntactic sugar, calling a setter method on an object outside of the class definition is a breeze! Let’s say we rip one of the pages out of our book.
设置器采用一个参数,即用户想要分配给实例变量的新值。 使用Ruby的语法糖,在类定义之外的对象上调用setter方法很容易! 假设我们从书中撕下其中一页。
triumph_of_the_city.pages = 337
Ruby allows the act of setting an instance variable of an object to look like assignment. Without the sugar, using the setter method looks like the code below.
Ruby允许将对象的实例变量设置为看起来像赋值的行为。 在没有糖的情况下,使用setter方法看起来像下面的代码。
triumph_of_the_city.pages=(337)
The syntax may take a little while to get used to, but let’s summarize everything we’ve done so far.
语法可能需要一点时间才能习惯,但让我们总结一下到目前为止已经完成的所有工作。
- We defined a class. This class is the template for all objects that are instantiated from it. In the class we dictate the possible attributes and the possible behaviors that objects made from that class will have. 我们定义了一个类。 此类是从其实例化的所有对象的模板。 在该类中,我们指示该类所创建的对象将具有的可能的属性和可能的行为。
We instantiated a
Book
object and gave it some unique values for instance variables.我们实例化了一个
Book
对象,并为它提供了一些实例变量的唯一值。- We defined a handful of getter methods to access the instance variables by calling a method on objects outside of the class definition. 通过在类定义之外的对象上调用方法,我们定义了一些getter方法来访问实例变量。
We defined a setter method we can use to re-assign the instance variable
pages
to a new value.我们定义了一个setter方法,可用于将实例变量
pages
重新分配给新值。
Let’s create one more Book
object and bind it to a local variable.
让我们再创建一个Book
对象,并将其绑定到局部变量。
walkable_city = Book.new('Walkable City Rules', 'Jeff Speck', 290)
We now have two different instances of the Book
class. They have different titles, authors, and numbers of pages. In other words, they are two different Book
objects that each have a distinct state.
现在,我们有两个不同的Book
类实例。 他们有不同的标题,作者和页数。 换句话说,它们是两个不同的Book
对象,每个对象都有不同的状态 。
*We will forego talking about attr_*
methods — Ruby’s short-hand option for creating getters and setters — in this article.
*在本文中,我们将不再讨论attr_*
方法(这是Ruby创建getter和setter的捷径)。
实例方法提供功能 (Instance methods give functionality)
Right now, we have two Book
objects. We can create a new book by passing arguments into the ::new
method call on the Book
class. Let’s think about the functionalities available for any Book
objects.
现在,我们有两个Book
对象。 我们可以通过将参数传递给Book
类的::new
方法调用来创建一本Book
。 让我们考虑一下可用于任何Book
对象的功能。
Because we wrote getter instance methods for title
, author
, and pages
we can access the values of each of these instance variables from outside the class. We also wrote a setter method for pages
, which allows us to change the value bound to @pages
in any Book
object to any value we desire. We have read and write access for the pages
instance variable, while we have read-only
access to title
and author
.
因为我们为title
, author
和pages
编写了getter实例方法,所以我们可以从类外部访问每个实例变量的值。 我们还为pages
写了一个setter方法,该方法允许我们将绑定到任何Book
对象中@pages
的值更改为@pages
任何值。 我们具有对pages
实例变量的读写访问权限,而具有对title
和author
read-only
访问权限。
With our setter method for @pages
, we can alter the number of pages. In fact, a user of our program can even set the number of pages to a String
object of 'hello'
.
使用我们的@pages
的setter方法,我们可以更改页面数。 实际上,我们程序的用户甚至可以将页面数设置为'hello'
的String
对象。
walkable_city.pages = 'hello'
Assigning a String
to a variable that is intended to represent a number of pages in a book is nonsensical. Let’s put some restrictions on what a user can do with the @pages
instance variable. Rather than allowing a user to set the value to anything they please, let’s only allow them to choose an Integer
object.
将String
分配给旨在代表一本书中许多页面的变量是没有意义的。 让我们对用户可以使用@pages
实例变量进行一些限制。 与其允许用户将值设置为任意值,不如让用户选择一个Integer
对象。
def pages=(new_pages)
if new_pages.instance_of?(Integer)
@pages = new_pages
else
puts 'Sorry, please enter a number'
end
end
We use a simple if-else
conditional that calls Object#instance_of?
on the object bound to the passed in parameter new_pages
. To the instance_of?
call, we pass in the argument of the class name Integer
. This is a predicate method that will return true
or false
, depending whether the class of the calling object matches the passed in argument. If new_pages
is bound to anInteger
object, we point the instance variable @pages
to that Integer
. If the user tries to pass in any other type of object, like a String
or a Hash
, we output an error message to the terminal and do not assign any value to @pages
.
我们使用一个简单的if-else
条件调用Object#instance_of?
在绑定到传入参数new_pages
的对象上。 到instance_of?
调用时,我们传入类名Integer
的参数。 这是一个谓词方法,该方法将返回true
或false
,具体取决于调用对象的类是否与传入的参数匹配。 如果将new_pages
绑定到Integer
对象,则将实例变量@pages
指向该Integer
。 如果用户尝试传入任何其他类型的对象(例如String
或Hash
,我们将向终端输出错误消息,并且不为@pages
分配任何值。
We have effectively put one protective measure in place to ensure that @pages
will always point to an Integer
object. This makes for a more robust program. If we have another instance method that makes use of @pages
, we can rest a little more assured that we can expect an Integer
and not an object from some other class. This will keep our program from breaking.
我们已经有效地采取了一项保护措施,以确保@pages
始终指向一个Integer
对象。 这使得程序更健壮。 如果我们还有另一个使用@pages
实例方法,我们可以放心一点,我们可以期待一个Integer
而不是其他类的对象。 这将防止我们的程序中断。
Now that we have a small, functioning Book
class, a couple Book
objects with their own unique instance variables and a shared set of methods (just the getters and setter), we can take a look at the main ideas of OOP and its main benefits as a programming paradigm in the second article of this series here.
现在我们有了一个小的,可以运行的Book
类,几个Book
对象,它们具有自己的唯一实例变量和一组共享的方法(只是getter和setter),我们可以看看OOP的主要思想及其主要优点作为本系列的第二篇文章中一种编程范式这里 。
翻译自: https://medium.com/@aumi9292/part-1-an-intro-to-oop-d95aa6e445ce