Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Object-Oriented Programming

Functional Object-Oriented Programming

One of the only certainties of software development is change. This change can often be painful because it means that we have to refactor or rewrite large quantities of our application. In order to ease the pain, programmers have developed a number of rules and techniques and codified them as "Best Practices". This is especially true in Object Oriented Programming.

Using real world examples we will discuss the strengths and weaknesses of OOP and explore the strengths and benefits of applying functional programming to our OOP code.

Chris Keathley

July 18, 2016
Tweet

More Decks by Chris Keathley

Other Decks in Programming

Transcript

  1. C

  2. int main(int argc, char** argv) { mpc_parser_t* Number = mpc_new("number");

    mpc_parser_t* Symbol = mpc_new("symbol"); mpc_parser_t* Sexpr = mpc_new("sexpr"); mpc_parser_t* Qexpr = mpc_new("qexpr"); mpc_parser_t* Expr = mpc_new("expr"); mpc_parser_t* Lispy = mpc_new("lispy"); mpca_lang(MPC_LANG_DEFAULT, " \ number : /-?[0-9]+/ ; \ symbol : '+' | '-' | '*' | '/' ; \ sexpr : '(' <expr>* ')' ; \ qexpr : '{' <expr>* '}' ; \ expr : <number> | <symbol> | <sexpr> ; \ lispy : /^/ <expr>* /$/ ; \ ", Number, Symbol, Sexpr, Qexpr, Expr, Lispy); puts("Crisp Version 0.0.0.1"); puts("Press Ctrl+c to Exit\n"); while (1) { char* input = readline("crisp> "); add_history(input); mpc_result_t r; if (mpc_parse("<stdin>", input, Lispy, &r)) { lval* x = lval_eval(lval_read(r.output)); lval_println(x); lval_del(x); mpc_ast_delete(r.output); } else { mpc_err_print(r.error); mpc_err_delete(r.error);
  3. int main(int argc, char** argv) { mpc_parser_t* Number = mpc_new("number");

    mpc_parser_t* Symbol = mpc_new("symbol"); mpc_parser_t* Sexpr = mpc_new("sexpr"); mpc_parser_t* Qexpr = mpc_new("qexpr"); mpc_parser_t* Expr = mpc_new("expr"); mpc_parser_t* Lispy = mpc_new("lispy"); mpca_lang(MPC_LANG_DEFAULT, " \ number : /-?[0-9]+/ ; \ symbol : '+' | '-' | '*' | '/' ; \ sexpr : '(' <expr>* ')' ; \ qexpr : '{' <expr>* '}' ; \ expr : <number> | <symbol> | <sexpr> ; \ lispy : /^/ <expr>* /$/ ; \ ", Number, Symbol, Sexpr, Qexpr, Expr, Lispy); puts("Crisp Version 0.0.0.1"); puts("Press Ctrl+c to Exit\n"); while (1) { char* input = readline("crisp> "); add_history(input); mpc_result_t r; if (mpc_parse("<stdin>", input, Lispy, &r)) { lval* x = lval_eval(lval_read(r.output)); lval_println(x); lval_del(x); mpc_ast_delete(r.output); } else { mpc_err_print(r.error); mpc_err_delete(r.error); wtfbbq
  4. OBJECT ORIENTED LANGUAGES HAVE… ▸ Behavior + Data (encapsulation) ▸

    Classes ▸ Objects (instances of classes) ▸ Inheritance ▸ Methods ▸ Polymorphism
  5. THE CLASSICS ▸ Design Patterns (Gang of 4) ▸ Growing

    Object-Oriented Software with Tests ▸ Practical Object-Oriented Design in Ruby ▸ Domain Driven Design ▸ Patterns of Enterprise Application Architecture
  6. BEST PRACTICES ▸ SOLID ▸ DRY ▸ Law of Demeter

    ▸ Small Methods ▸ Design by Contract ▸ TDD ▸ BDD ▸ Commands and Queries ▸ Tell don’t ask ▸ Separation of Concerns
  7. “the first 90% of all software products is the MVP,

    the other 90% is maintenance” - Greg O.
  8. Pure Functions # Pure def add(x, y) x + y

    end # Impure def user_input_add(x) print "Enter a number: " num = gets puts x + num.to_i end
  9. Immutability test "users can be fed" do user = User.new

    assert user.hungry? == true user.feed(Hamburger.new) assert user.hungry? == false end class User def initialize(stomach: []) @stomach = stomach end def feed(item) @stomach << item end end
  10. Immutability test "users can be fed" do user = User.new

    assert user.hungry? == true fed_user = user.feed(Hamburger.new) assert fed_user.hungry? == false end class User def initialize(stomach: []) @stomach = stomach end def feed(item) @stomach << item end end
  11. Immutability test "users can be fed" do user = User.new

    assert user.hungry? == true fed_user = user.feed(Hamburger.new) assert fed_user.hungry? == false end class User def initialize(stomach: []) @stomach = stomach end def feed(item) User.new(stomach: @stomach + [item]) end end
  12. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end
  13. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def self.notify(user) email!(user.email, @ticket.id, @ticket.details) end end
  14. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end
  15. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end arrangement
  16. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end arrangement Work
  17. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert end end

    class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert OnCallStaff.all.each do |user| @notifier.notify(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end
  18. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) OnCallStaff.all.each do |user|

    TicketNotifier.new(TicketAlertMailer.new(ticket)).send_alert(user) end end end class TicketNotifier def initialize(notifier) @notifier = notifier end def send_alert(user) @notifier.notify(user) end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end
  19. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) OnCallStaff.all.each do |user|

    TicketAlertMailer.new(ticket).send_alert(user) end end end class TicketAlertMailer def initialize(ticket) @ticket end def notify(user) email!(user.email, @ticket.id, @ticket.details) end end
  20. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) OnCallStaff.all.each do |user|

    TicketAlertMailer.notify(user, ticket) end end end class TicketAlertMailer def notify(user, ticket) email!(user.email, ticket.id, ticket.details) end end
  21. class NewTicketService def self.create_ticket(ticket_params) ticket = Ticket.create(ticket_params) OnCallStaff.all.each do |user|

    TicketAlertMailer.notify(user, ticket) SlackNotifier.notify(user, ticket) end end end class TicketAlertMailer def notify(user, ticket) email!(user.email, ticket.id, ticket.details) end end class SlackNotifier def self.notify(user, ticket) send_slack_message(user.slack_name, ticket.details) end end