Git Product home page Git Product logo

delete-phase-3-ruby-oo-fundamentals-object-attributes's Introduction

Object Attributes

Objectives

  1. Use methods to abstract or wrap the attributes of an object.
  2. Explain the difference between a setter and a getter method.
  3. Build and use setter and getter methods

Introduction

So far, we've learned how to build classes and even how to give our classes instance methods. Now, we can add the special method initialize, which will require certain arguments to be passed when instantiating the class to provide it with initial data. In this example, our Person class has an instance method, #name, that is set each time a new Person class is created. This name method can be called on an instance of Person (an individual person object) and return that person's name.

class Person
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

kanye = Person.new("Kanye")
kanye.name #=> "Kanye"

But wait! Kanye has decided he wants to be referred to as "Yeezy". Kanye is a huge star so we should probably do what he says. However, as it currently stands, we don't have a way to re-assign Kanye's name. Let's see what happens when we try:

kanye.name = "Yeezy" 
#=> NoMethodError: undefined method `name=' for #<Person:0x007fd7cbb0f3d8>

We get a no method error! Kanye's name change is just one example of the many common situations in which we might want to alter the information or attributes associated with a given object. Let's move on to the next section to learn how to add that functionality to our classes.

Setter vs. Getter Methods

Our Person class' #name method is referred to as a "getter" or reader method. It returns information stored in an instance variable. In order to make a person's name attribute writable, we need to define a "setter" or writer method.

Defining a Setter Method

class Person

  def initialize(name)
    @name = name
  end

  def name
    @name
  end

  def name=(new_name)
    @name = new_name
  end

end

A setter method is defined with an =, equals sign, appended to the name of the method. The = is followed by the (argument_name). Now that we've defined our setter method on the Person class, we can change Kanye's name.

Note: The syntax highlighting may make it look like the = sign in name= is separate from the name of the definition, but it is not. The = is a necessary piece. Adding it in allows us to write kanye.name = "Yeezy" but this is a bit of syntatic sugar. Writing kanye.name=("Yeezy") is also a valid way to use this setter method.

Calling a Setter Method

To call a setter method, you use the . notation (dot notation) to call the method and set it equal to a new value.

kanye = Person.new("Kanye")

kanye.name
  => "Kanye"

kanye.name = "Yeezy"
kanye.name
  => "Yeezy"

Let's break it down. We:

  • Instantiate a Person instance and name him "Kanye".
  • Call our getter method, #name to return his name, "Kanye"
  • Call our setter method #name= to change his name to "Yeezy"
  • Call our getter method again and see that kanye's name is now "Yeezy".

You can also call a setter method like this:

kanye.name=("Yeezy")

But we prefer the first notation.

The Abstraction of Instance Methods

In Ruby, it is possible to simply set an instance variable with the following method:

kanye.instance_variable_set(:@name, "Yeezy")

It is also possible to simply retrieve an instance variable from an object with the following method:

kanye.instance_variable_get(:@name)
  => "Yeezy"

Since this is the case, why do we even use instance setter and getter methods? In fact, there are a number of reasons:

Syntactic Vinegar vs. Syntactic Sugar

The first reason is a stylistic one but it is important. As object-oriented Rubyists, we care about our program's readability and design. The above method is ugly. It places a verb at the end of the method name. The weird grammar of this method should remind us not to use it. (This technique of purposely naming methods in a hard to use manner is called syntactic vinegar.)

Exposing Literal Variables vs. Abstracting Attributes

The instance_variable_set method depends on a literal, concrete variable, @name. It exposes it directly to the person executing our code. This is bad practice because it forces our program to rely directly on the @name variable. Why is this so terrible? Let's take a look at the following use case:

For example, Kanye (who by the way has commissioned you to write this amazing Person class program) has decided that our program should store both a first and last name. Let's do a quick refactor of our Person class.

We'll initialize our Person instances with both a first and last name.

class Person

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  #...

end

With this change, our program does more than just make Kanye happy. It has some added functionality. We could imagine collecting all of our instances of Person and sorting them by last name, for example.

BUT, now, any other part of our program that was calling instance_variable_get(:@name) is broken! Additionally, any part of our program that is calling instance_variable_set(:@name) isn't taking advantage of our new first name and last name functionality. Any attempt to change a person's name with instance_variable_set(:@name) wouldn't really change their name, because it wouldn't touch the @first_name and @last_namevariables set with our initialize method. It would just give them a @name variable set to a different value than the @first_name and @last_name variables. That would get confusing, fast.

Allowing our code to rely on an instance variable directly created a program that is not flexible. If our program contains multiple occurrences of instance_variable_get(:@name) and instance_variable_set(:@name), we would have to hunt down each and every one and change them to accommodate our shift to using both a first and a last name.

Instead of writing code that depends on instance (or any type of) variables, we write methods that contain instance variables. This is a form of abstraction, whereas the instance variable @name is a literal value. The literal value reference, the variable @name, may change as our application grows and we want our application to seamlessly accommodate that change.

Let's create our abstraction: the #name= and #name setter and getter instance methods:

class Person

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def name=(full_name)
    first_name, last_name = full_name.split
    @first_name = first_name
    @last_name = last_name
  end

  def name
    "#{@first_name} #{@last_name}".strip
  end

end

Now, even if the content of the #name method changes, for example, Kanye changes his mind again and wants to be referred to only as "Yeezy" (using our interface this change would be kanye.name = "Yeezy"), the interface, how our application uses that content, remains constant. In other words, we can change the content of these methods according to our needs, without needing to hunt down every appearance of them in our program and change them as well, like we would need to do with our instance_method_set and instance_method_get usages.

Coming Up

In the following lab, you'll be defining your own class and setter and getter methods. Then, we'll discuss yet another level of abstraction dealing with these method types.

delete-phase-3-ruby-oo-fundamentals-object-attributes's People

Contributors

alexgriff avatar annjohn avatar aviflombaum avatar babyshoes avatar deniznida avatar gilmoursa avatar jeffkatzy avatar jerrystsai avatar jonbf avatar lizbur10 avatar maxwellbenton avatar morgvanny avatar msuzoagu avatar nancyko avatar octosteve avatar pletcher avatar sandyvern avatar sarogers avatar sgharms avatar sophiedebenedetto avatar talum avatar timothylevi avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

lisswart

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.