Welcome to today's lesson, where we will tackle a common challenge in software engineering: the introduction of complex features while preserving backward compatibility. Our focus will be on a Potluck Dinner organization system, where we will manage participants and their respective dishes for each round. Get ready for an exciting journey through Ruby programming, step-by-step analysis, and strategic thinking. Let's dive into our adventure!
Initially, our Potluck Dinner organization system allows us to add and remove participants and manage their respective dishes for each round. There are three essential methods:
add_participant(member_id)
: This method adds a participant. If a participant with the givenmember_id
already exists, it won't create a new one but will returnfalse
. Otherwise, it will add the member and returntrue
.remove_participant(member_id)
: This method removes a participant with the givenmember_id
. If the participant exists, the system will remove them and returntrue
. Otherwise, it will returnfalse
. When removing a participant, you need to remove their dish if they brought one.add_dish(member_id, dish_name)
: This method enables each participant to add their dishes for every round. If a participant has already added a dish for this round OR if themember_id
isn't valid, the method will returnfalse
. Otherwise, it will add the dish for the respective participant's round and returntrue
.
Let's write our Ruby code, which implements the functions as per our initial state:
Ruby1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 end 6 7 def add_participant(member_id) 8 return false if @participants.key?(member_id) 9 10 @participants[member_id] = true 11 true 12 end 13 14 def remove_participant(member_id) 15 return false unless @participants.key?(member_id) 16 17 @participants.delete(member_id) 18 @dishes.delete(member_id) 19 true 20 end 21 22 def add_dish(member_id, dish_name) 23 return false unless @participants.key?(member_id) 24 return false if @dishes.key?(member_id) 25 26 @dishes[member_id] = dish_name 27 true 28 end 29end
In this code, we employed Ruby hashes to store unique participant IDs and their respective dish names. With this foundation laid, let's introduce some advanced functionalities.
Our Potluck Dinner organization system is currently simple but practical. To make it even more exciting, we're going to introduce a "Dish of the Day" feature. This feature will enable participants to vote, and the dish receiving the most votes will be declared the "Dish of the Day."
To add this feature, we will define two new methods:
vote(member_id, vote_id)
: This method will allow a participant to cast a vote for a dish. Each participant can cast a vote only once per round. If a participant tries to vote again or if themember_id
isn't valid, it should returnfalse
.dish_of_the_day
: This method will calculate and return the "Dish of the Day" based on the votes received. If multiple dishes tie for the highest number of votes, the dish brought by the participant who joined first acquires precedence. If there are no votes, the function returnsnil
.
First, we update our Potluck
class to store the participant's joining time directly in the participants
hash, and ensure compatibility across other methods.
Constructor Update:
Ruby1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 @votes = {} 6 end 7end
Updating Methods to Incorporate Join Time and Consistent Removal:
Ruby1def add_participant(member_id) 2 return false if @participants.key?(member_id) 3 4 @participants[member_id] = { join_time: Time.now.to_f } 5 true 6end 7 8def remove_participant(member_id) 9 return false unless @participants.key?(member_id) 10 11 @participants.delete(member_id) 12 @dishes.delete(member_id) 13 @votes.delete(member_id) 14 true 15end 16 17def add_dish(member_id, dish_name) 18 return false unless @participants.key?(member_id) 19 return false if @dishes.key?(member_id) 20 21 @dishes[member_id] = dish_name 22 true 23end
By storing a join_time
in the participants
hash, we keep track of who joined first. We also ensure removal from all related structures (@dishes
and @votes
) for consistency.
Next, we add a method to allow participants to vote for the dishes:
Ruby1def vote(member_id, vote_id) 2 if @participants.key?(member_id) && !@votes.key?(member_id) 3 @votes[member_id] = vote_id 4 true 5 else 6 false 7 end 8end
This ensures each participant can only vote once per round, returning false
if the participant has already voted or if the participant doesn't exist.
Finally, we implement the logic to determine the "Dish of the Day":
Ruby1def dish_of_the_day 2 return nil if @votes.empty? 3 4 vote_count = Hash.new(0) 5 @votes.values.each { |vote| vote_count[vote] += 1 } 6 7 max_votes = vote_count.values.max 8 max_vote_dishes = vote_count.select { |_, count| count == max_votes }.keys 9 10 earliest_join_time = Float::INFINITY 11 dish_of_the_day_member = nil 12 max_vote_dishes.each do |member_id| 13 if @participants[member_id][:join_time] < earliest_join_time 14 earliest_join_time = @participants[member_id][:join_time] 15 dish_of_the_day_member = member_id 16 end 17 end 18 19 "Participant: '#{dish_of_the_day_member}', Dish: '#{@dishes[dish_of_the_day_member]}'" 20end
If there are no votes, the method returns nil
. In case of a tie, the participant with the earliest join time wins.
- Participants Data Structure Change: By using a hash for participant details, we ensure each participant remains uniquely identifiable while enabling additional functionality like join time.
- Method Signature Consistency: All method signatures remain unchanged to ensure existing interactions remain unaffected.
Combining all our steps, this is the final implementation of our Potluck class with all the required methods:
Ruby1class Potluck 2 def initialize 3 @participants = {} 4 @dishes = {} 5 @votes = {} 6 end 7 8 def add_participant(member_id) 9 return false if @participants.key?(member_id) 10 11 @participants[member_id] = { join_time: Time.now.to_f } 12 true 13 end 14 15 def remove_participant(member_id) 16 return false unless @participants.key?(member_id) 17 18 @participants.delete(member_id) 19 @dishes.delete(member_id) 20 @votes.delete(member_id) 21 true 22 end 23 24 def add_dish(member_id, dish_name) 25 return false unless @participants.key?(member_id) 26 return false if @dishes.key?(member_id) 27 28 @dishes[member_id] = dish_name 29 true 30 end 31 32 def vote(member_id, vote_id) 33 if @participants.key?(member_id) && !@votes.key?(member_id) 34 @votes[member_id] = vote_id 35 true 36 else 37 false 38 end 39 end 40 41 def dish_of_the_day 42 return nil if @votes.empty? 43 44 vote_count = Hash.new(0) 45 @votes.values.each { |vote| vote_count[vote] += 1 } 46 47 max_votes = vote_count.values.max 48 max_vote_dishes = vote_count.select { |_, count| count == max_votes }.keys 49 50 earliest_join_time = Float::INFINITY 51 dish_of_the_day_member = nil 52 max_vote_dishes.each do |member_id| 53 if @participants[member_id][:join_time] < earliest_join_time 54 earliest_join_time = @participants[member_id][:join_time] 55 dish_of_the_day_member = member_id 56 end 57 end 58 59 "Participant: '#{dish_of_the_day_member}', Dish: '#{@dishes[dish_of_the_day_member]}'" 60 end 61end
Bravo! You've successfully completed the task of introducing complex features while preserving backward compatibility using Ruby. This skill is invaluable in real-life software engineering scenarios, where maintaining existing functionality while enhancing systems is crucial. Strengthen this ability with even more practice and explore similar challenges. I'll see you in the next lesson! Happy coding with Ruby!