Skip to content

Instantly share code, notes, and snippets.

@gsmendoza
Created June 10, 2016 01:59
Show Gist options
  • Save gsmendoza/f9d31c700823b6169b0e7c3f1cdead05 to your computer and use it in GitHub Desktop.
Save gsmendoza/f9d31c700823b6169b0e7c3f1cdead05 to your computer and use it in GitHub Desktop.
Ruby implementation of weather monitoring app from Head First Design Patterns, Chapter 2: The Observer Pattern
require "observer"
require 'attr_extras'
module WeatherMonitoringApp
module HeatIndex
# rubocop:disable Metrics/AbcSize
def self.calculate(t, rh)
(
16.923 +
(0.185212 * t) +
(5.37941 * rh) -
(0.100254 * t * rh) +
(0.00941695 * (t * t)) +
(0.00728898 * (rh * rh)) +
(0.000345372 * (t * t * rh)) -
(0.000814971 * (t * rh * rh)) +
(0.0000102102 * (t * t * rh * rh)) -
(0.000038646 * (t * t * t)) +
(0.0000291583 * (rh * rh * rh)) +
(0.00000142721 * (t * t * t * rh)) +
(0.000000197483 * (t * rh * rh * rh)) -
(0.0000000218429 * (t * t * t * rh * rh)) +
(0.000000000843296 * (t * t * rh * rh * rh))
) -
(0.0000000000481975 * (t * t * t * rh * rh * rh))
end
# rubocop:enable Metrics/AbcSize
end
module DisplayElement
def display
raise NotImplementedError
end
end
class WeatherData
include Observable
def set_measurements(temperature, humidity, pressure)
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
measurements_changed
end
def measurements_changed
changed
notify_observers(temperature, humidity, pressure)
end
private
attr_accessor :temperature, :humidity, :pressure
end
class CurrentConditionsDisplay
include DisplayElement
def initialize(weather_data)
self.weather_data = weather_data
weather_data.add_observer(self)
end
def update(temperature, humidity, _pressure)
self.temperature = temperature
self.humidity = humidity
display
end
def display
puts "Current conditions: #{temperature}F degrees and #{humidity}\% humidity"
end
private
attr_accessor :temperature, :humidity, :weather_data
end
class HeatIndexDisplay
include DisplayElement
def initialize(weather_data)
self.weather_data = weather_data
weather_data.add_observer(self)
end
def update(temperature, humidity, _pressure)
self.temperature = temperature
self.humidity = humidity
display
end
def display
puts "Head index is #{HeatIndex.calculate(temperature, humidity)}"
end
private
attr_accessor :temperature, :humidity, :weather_data
end
class ForecastDisplay
include DisplayElement
def initialize(weather_data)
self.current_pressure = 29.92
self.weather_data = weather_data
weather_data.add_observer(self)
end
def update(_temperature, _humidity, pressure)
self.last_pressure = current_pressure
self.current_pressure = pressure
display
end
def display
print "Forecast: "
if current_pressure > last_pressure
puts "Improving weather on the way!"
elsif current_pressure == last_pressure
puts "More of the same"
elsif current_pressure < last_pressure
puts "Watch out for cooler, rainy weather"
end
end
private
attr_accessor :last_pressure, :current_pressure, :weather_data
end
class WeatherStation
def self.main
weather_data = WeatherData.new
CurrentConditionsDisplay.new(weather_data)
HeatIndexDisplay.new(weather_data)
ForecastDisplay.new(weather_data)
weather_data.set_measurements(80, 65, 30.4)
weather_data.set_measurements(82, 70, 29.2)
weather_data.set_measurements(78, 90, 29.2)
end
end
end
WeatherMonitoringApp::WeatherStation.main
@bigxiang
Copy link

With attr_extras, perhaps you can try attr_implement 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment