Last active
May 2, 2024 15:47
-
-
Save jrosell/f2e71b5850845b31952c11b7183eaea0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env Rscript | |
cat("==========================================================================\n") | |
cat("Example of strategy patern in R: Multiple ways to show routes. \n") | |
# Problem: | |
# You build a navigation app for casual travelers and help users quickly orient themselves in any city. | |
# | |
# - The first only build the routes over roads. | |
# - Next update, you added an option to build walking routes. | |
# - Right after that, you added another option to let people use public transport in their routes. | |
# - Later you planned to add route building for cyclists. | |
# | |
# Each time you added a new routing algorithm, the navigator function increased the size. | |
# Any change to one of the algorithms, affected the whole function, increasing the chance of creating an error in already-working code. | |
# Implementing a new feature requires you to change the same huge function, conflicting with the code produced by other people. | |
# | |
# Solution in R: | |
# Take a function with a lot of conditionals or branchs and move them into separate implementations. | |
# We can use generic functions to write strategy implementations in each concrete method of each strategy. | |
# | |
# The main function would be isn’t responsible for selecting an appropriate algorithm for the job. | |
# In fact, the main function doesn’t know much about strategies. | |
# Is the client who passes the object as argument with the desired strategy for the generic function. | |
# | |
# In our navigation app each routing algorithm can be extracted to its buildRoute implementation. | |
# Even though given the same arguments, each routing class might build a different route, the main navigator function. | |
# doesn’t really care which algorithm is selected since its primary job is to get a set of checkpoints on the map. | |
# The class has a property for switching the active routing strategy, so its clients in the | |
# user interface, can replace the currently selected routing behavior with another one. | |
select_route_strategy <- \() { | |
strategy_class <- "" | |
while(strategy_class == "") { | |
cat("\nWhat option do you want?\nHere are the options: 1) roads 2) walk.\n"); | |
strategy_selection <- readLines("stdin", n = 1); | |
strategy_class <- dplyr::case_match( | |
strategy_selection, | |
"1" ~ "routes_roads", | |
"2" ~ "routes_walking", | |
.default = "" | |
) | |
} | |
return(structure(list(), class = strategy_class)) | |
} | |
new_context <- \(data, routes_strategy = NULL) { | |
structure(list(data = data, routes_strategy = routes_strategy), class = "context") | |
} | |
buildRoute <- \(x, context) { | |
UseMethod("buildRoute") | |
} | |
buildRoute.default <- \(x, context) { | |
stop("You must select a valid route_strategy") | |
} | |
buildRoute.routes_roads <- \(x, context) { | |
subset(context$data, y == 1) | |
} | |
buildRoute.routes_walking <- \(x, context) { | |
subset(context$data, y == 2) | |
} | |
navigator <- \(context){ | |
buildRoute(context$routes_strategy, context) | |
} | |
cat("Give me 4 numbers:\n"); | |
x <- readLines("stdin", n = 4); | |
df <- data.frame(x = x, y = c(1, 1, 2, 2)) | |
repeat { | |
route_strategy <- select_route_strategy() | |
context <- new_context( | |
data = df, | |
routes_strategy = route_strategy | |
) | |
print(navigator(context)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment