Ruby中ProcessTrip类的total_amount_collected为何被判定为私有方法?
问题:未声明private的实例方法为何被视为私有方法?
报错信息
调用total_amount_collected方法时触发如下错误:
trip.total_amount_collected NoMethodError: private method `total_amount_collected' called for #<ProcessTrip:0x0000000110e33da0> from (pry):3:in `get_total_collection'
调用代码
def get_total_collection trip = ProcessTrip.new trip.total_amount_collected sort_passengers(total_collection); print_summary(total_collection) if @print_summary end
相关类代码
ProcessTrip类(含错误写法)
class ProcessTrip < MetroCardClassMethods attr_accessor :travel_history, :station, :destination def initialize end def update_balace_aumount(balance_details,trip_to,trip_complete) effective_tarif = trip_complete ? passenger_tarif_for(_passenger_type) / 2 : passenger_tarif_for(_passenger_type) trip_to['discount']= effective_tarif if trip_complete trip_to['discount']= 0 if !trip_complete if balance_details["balance"].to_i < effective_tarif effective_trip_cost = effective_tarif - balance_details["balance"].to_i trip_to["trip_cost"] += effective_trip_cost*0.02 balance_details["balance"] = 0; else balance_details["balance"] = balance_details["balance"].to_i - effective_tarif end end end def total_amount_collected () @@vertex.each do |metro_id, travel_history| result = find_amount_collected_for_metrocard(self.class.card_list[metro_id], travel_history) result.each do |key,value| self.class.collection[key]["cost"]+=value["trip_cost"]; self.class.collection[key]["discount"]+=value["discount"]; self.class.collection[key]["passengers"]+=value["passenger"]; end end return @@collection end def find_amount_collected_for_metrocard(balance_details, trip_info) trip_from={} trip_info.each do |station, destination| @station = station if _is_return_journey(trip_from) process_trip_segment(trip_from,_destination,_station) trip_from[_station]["trip_cost"] = passenger_tarif_for(_passenger_type) * 0.5 update_balace_aumount(balance_details, trip_from[_station], true); else process_trip_segment(trip_from,_station,_station) trip_from[_station]["trip_cost"] = (trip_from[_station]["trip_cost"] || 0) + passenger_tarif_for(_passenger_type) update_balace_aumount(balance_details, trip_from[_station], false); end end return trip_from end
MetroCardClassMethods父类
class MetroCardClassMethods @@card_list = [] @@vertex = {}; @@collection={"CENTRAL"=>{"cost"=>0,"discount"=>0,"passengers"=>[]},"AIRPORT"=>{"cost"=>0,"discount"=>0,"passengers"=>[]}} @@station_from = {"CENTRAL"=>"AIRPORT","AIRPORT"=>"CENTRAL"} @@station = [] def initialize(item) @travel_history = {} @metro_id = item["metro_id"] @station = item["station"] @passenger_type = item["passenger_type"] @destination = @@station_from[@station] end class << self def card_list=(card_list) @@card_list=card_list end def card_list @@card_list end def collection @@collection end def station_from @@station_from end def vertex @@vertex end end end
原因解析
核心问题是代码结构错误:
total_amount_collected和find_amount_collected_for_metrocard被定义在了ProcessTrip类的外部(类的end语句之后),这会导致这两个方法被自动添加到Object类中。- Ruby顶级作用域中,直接定义的方法默认是
Object类的私有实例方法——因为顶级作用域的self是main(属于Object的实例),这类方法会被默认设为私有。 - 当用
ProcessTrip实例调用total_amount_collected时,Ruby会沿继承链查找:ProcessTrip→MetroCardClassMethods→Object,最终找到Object中的私有方法,因此触发私有方法调用错误。 - 而
update_balace_aumount是在ProcessTrip类内部定义的,属于ProcessTrip的公开实例方法,所以可以正常调用。
修复方案
将total_amount_collected和find_amount_collected_for_metrocard移动到ProcessTrip类的end之前,确保它们属于ProcessTrip类的实例方法:
class ProcessTrip < MetroCardClassMethods attr_accessor :travel_history, :station, :destination def initialize end def update_balace_aumount(balance_details,trip_to,trip_complete) effective_tarif = trip_complete ? passenger_tarif_for(_passenger_type) / 2 : passenger_tarif_for(_passenger_type) trip_to['discount']= effective_tarif if trip_complete trip_to['discount']= 0 if !trip_complete if balance_details["balance"].to_i < effective_tarif effective_trip_cost = effective_tarif - balance_details["balance"].to_i trip_to["trip_cost"] += effective_trip_cost*0.02 balance_details["balance"] = 0; else balance_details["balance"] = balance_details["balance"].to_i - effective_tarif end end def total_amount_collected () @@vertex.each do |metro_id, travel_history| result = find_amount_collected_for_metrocard(self.class.card_list[metro_id], travel_history) result.each do |key,value| self.class.collection[key]["cost"]+=value["trip_cost"]; self.class.collection[key]["discount"]+=value["discount"]; self.class.collection[key]["passengers"]+=value["passenger"]; end end return @@collection end def find_amount_collected_for_metrocard(balance_details, trip_info) trip_from={} trip_info.each do |station, destination| @station = station if _is_return_journey(trip_from) process_trip_segment(trip_from,_destination,_station) trip_from[_station]["trip_cost"] = passenger_tarif_for(_passenger_type) * 0.5 update_balace_aumount(balance_details, trip_from[_station], true); else process_trip_segment(trip_from,_station,_station) trip_from[_station]["trip_cost"] = (trip_from[_station]["trip_cost"] || 0) + passenger_tarif_for(_passenger_type) update_balace_aumount(balance_details, trip_from[_station], false); end end return trip_from end end # 该end属于ProcessTrip类
内容的提问来源于stack exchange,提问作者Moin Ahmed
相关产品推荐
相关产品推荐

