Search code examples
rubyclassmethodsinstances

How to count instances of class without counting reassignment?


I am working on class methods.

I am trying to count the number of created instances of a class. I am able to do this by creating a counter variable in the initialize method.

The problem arises when I reassign the variable originally assigned to one class instance. Because the initialize method is called twice, it does not recognize that the variable is simply being reassigned to another class instance.

class Ticket
    attr_accessor :price
    attr_reader :event, :venue

    @@count = 0
    @@tickets = {}

    def initialize(event, venue)
        @event = event
        @venue = venue

        @@count += 1
    end

    def self.count
        @@count 
    end
end

a = Ticket.new("Michael Buble", "Staples")

a = Ticket.new("Frank Sinatra", "Madison Square Garden")

puts "Ticket count of #{Ticket.count}"

When I run the above code in IRB, it gives me a Ticket count of 2 (as expected). How do I change my code so that it recognizes the overwrite?

NOTE: I know that this question has been asked before for Objective C, but the reassignment aspect of the question adds a different element to the problem. Let me know otherwise.


Solution

  • In the real world you wouldn't be counting instances in memory, you'd be asking a database how many exist. You need to think in terms of a database.

    Your use of a to repeatedly contain the Ticket instance is wrong. You should be using an Array, Hash or Set to maintain the list, then ask the container how many exist:

    require 'set'
    
    class Ticket
      attr_accessor :price
      attr_reader :event, :venue
    
      @@tickets = Set.new
    
      def initialize(event, venue)
        @event = event
        @venue = venue
    
        @@tickets << self
      end
    
      def delete
        @@tickets.delete(self)
      end
    
      def self.count
        @@tickets.size
      end
    
    end
    
    a = Ticket.new("Michael Buble", "Staples")
    b = Ticket.new("Frank Sinatra", "Madison Square Garden")
    
    puts "Ticket count of #{Ticket::count}"
    b.delete
    puts "Ticket count of #{Ticket::count}"
    

    You can build this out by adding ways to retrieve a particular instance from @@tickets, add a to_s so you can list them, but, in the end, you'll want to use a real database. If your code were to crash for any reason, your entire list of tickets would disappear, which would be unacceptable in real life.