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

Journey from Object Oriented language to Go

Journey from Object Oriented language to Go

During this talk speaker will share his experience of migrating from OOP style applications to the Golang. How view on doing things shifted through time and how it changed development. He will talk about success, struggle and mistakes. After this talk it will be easier for you to convince your friends/colleagues to move to the Golang.

Sergey Kibish

October 21, 2018
Tweet

More Decks by Sergey Kibish

Other Decks in Technology

Transcript

  1. In the end • Will to try Go • Make

    it 1# language of choice • Convince somebody to switch to Go
  2. Object-oriented programming (OOP) is a programming paradigm based on the

    concept of "objects", which may contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods
  3. public class Vehicle { protected final String brand; protected final

    Integer wheels; public Vehicle(String brand, Integer wheels) { this.brand = brand; this.wheels = wheels; } public Integer getSpeed() { return this.wheels * 5; } public String getBrand() { return this.brand; } }
  4. public class Vehicle { protected final String brand; protected final

    Integer wheels; public Vehicle(String brand, Integer wheels) { this.brand = brand; this.wheels = wheels; } public Integer getSpeed() { return this.wheels * 5; } public String getBrand() { return this.brand; } }
  5. public class Vehicle { protected final String brand; protected final

    Integer wheels; public Vehicle(String brand, Integer wheels) { this.brand = brand; this.wheels = wheels; } public Integer getSpeed() { return this.wheels * 5; } public String getBrand() { return this.brand; } }
  6. type Vehicle struct { brand string wheels int } func

    (v *Vehicle) GetBrand() string { return v.brand } func (v *Vehicle) GetSpeed() int { return v.wheels * 5 }
  7. type Vehicle struct { brand string wheels int } func

    (v *Vehicle) GetBrand() string { return v.brand } func (v *Vehicle) GetSpeed() int { return v.wheels * 5 }
  8. type Vehicle struct { brand string wheels int } func

    (v *Vehicle) GetBrand() string { return v.brand } func (v *Vehicle) GetSpeed() int { return v.wheels * 5 }
  9. public class Car extends Vehicle { public Car(String brand) {

    super(brand, 4); } } Car car = new Car("Ford"); System.out.println(car.getBrand()); System.out.println(car.getSpeed()); >> Ford >> 20
  10. public class Car extends Vehicle { public Car(String brand) {

    super(brand, 4); } @Override public Integer getSpeed() { return wheels * 3; } } Car car = new Car("Ford"); System.out.println(car.getBrand()); System.out.println(car.getSpeed()); >> Ford >> 12
  11. func (v *Vehicle) ComputeSpeed() int { return v.wheels * 5

    } type Speeder interface { GetSpeed() int } func (c *Car) GetSpeed() int { return c.wheels * 3 } func (m *Motorcycle) GetSpeed() int { return m.Base.ComputeSpeed() }
  12. func (v *Vehicle) ComputeSpeed() int { return v.wheels * 5

    } type Speeder interface { GetSpeed() int } func (c *Car) GetSpeed() int { return c.wheels * 3 } func (m *Motorcycle) GetSpeed() int { return m.Base.ComputeSpeed() } fmt.Println(car.GetSpeed()) fmt.Println(motorcycle.GetSpeed())
  13. type Speeder interface { GetSpeed() int } func printSpeed(s Speeder)

    { fmt.Println(s.GetSpeed()) } printSpeed(car) printSpeed(motorcycle)
  14. func NewFord() *Car { return &Car{ Vehicle{"Ford", 4}, } }

    func main() { car := NewFord() printSpeed(car) }
  15. @RestController @RequestMapping("/pages") public class PagesController { @Post public IdDto create()

    { return "id"; } @Get("/{id}") public PagesDto get(@PathVariable String id) { } }
  16. @JsonInclude(Include.NON_NULL) public class PagesDto { private String id; private String

    title; private String body; // getters and setter } @Entity public class PagesDao { @Id private String id; private String title; private String body; // getters and setters }
  17. @Service public class PagesService { @Autowire private PagesRepository repository; @Autowire

    private PagesMapper mapper; public String create(PagesDto dto) { PagesDao dao = mapper.map(dto); return repository.save(dao); } public PagesDto get(String id) { PagesDto dto = mapper.map(repository.getById(id)); return dto; }
  18. @RestController @RequestMapping("/pages") public class PagesController { @Autowire private PagesService service;

    @Post public IdDto create(@RequestBody PagesDto dto) { String id = service.create(dto); return new IdDto(id); } @Get("/{id}") public PagesDto get(@PathVariable String id) { return service.get(id); } }
  19. . !"" controllers # $"" PagesController.java !"" dao # $""

    PagesDao.java !"" dto # !"" IdDto.java # $"" PagesDto.java !"" mappers # $"" PagesMapper.java !"" repositories # $"" PagesRepository.java $"" services $"" PagesService.java
  20. Summary (good) • Framework was used • Fast and easy

    to develop • IoC out of the box (@Autowire)
  21. Summary (bad) • Annotations • Not transparent • A lot

    of things where you don’t have control (it just works) • Super high level of abstraction
  22. Simple: a line of code that does something very small

    and specific. Easy: a line of code that does a lot by calling a framework function causing thousands of lines of code to be executed. https://ilya-sher.org/2016/05/19/tips-for-beginning-systems-and-software-engineers/
  23. Easy: a line of code that does a lot by

    calling a framework function causing thousands of lines of code to be executed. https://ilya-sher.org/2016/05/19/tips-for-beginning-systems-and-software-engineers/
  24. package api // api/api.go type API struct { storage *storage.Storage

    server *http.Server } func NewAPI(storage *storage.Storage) *API { return &API{ storage: storage, } } func (a *API) Start(port string) error { a.server = &http.Server{ Addr: ":" + port, Handler: a.bootRouter(), } return a.server.ListenAndServe() } func (a *API) Shutdown() error { return a.server.Shutdown(context.Background()) } func (a *API) bootRouter() *httprouter.Router { router := httprouter.New() router.POST("/pages", a.Create) router.GET("/pages", a.GetAll) router.GET("/pages/:id", a.Get) router.PUT("/pages/:id", a.Update) router.DELETE("/pages/:id", a.Delete) return router }
  25. package api // api/api.go type API struct { storage *storage.Storage

    server *http.Server } func NewAPI(storage *storage.Storage) *API { return &API{ storage: storage, } } func (a *API) Start(port string) error { a.server = &http.Server{ Addr: ":" + port, Handler: a.bootRouter(), } return a.server.ListenAndServe() } func (a *API) Shutdown() error { return a.server.Shutdown(context.Background()) } func (a *API) bootRouter() *httprouter.Router { router := httprouter.New() router.POST("/pages", a.Create) router.GET("/pages", a.GetAll) router.GET("/pages/:id", a.Get) router.PUT("/pages/:id", a.Update) router.DELETE("/pages/:id", a.Delete) return router }
  26. package api // api/api.go type API struct { storage *storage.Storage

    server *http.Server } func NewAPI(storage *storage.Storage) *API { return &API{ storage: storage, } } func (a *API) Start(port string) error { a.server = &http.Server{ Addr: ":" + port, Handler: a.bootRouter(), } return a.server.ListenAndServe() } func (a *API) Shutdown() error { return a.server.Shutdown(context.Background()) } func (a *API) bootRouter() *httprouter.Router { router := httprouter.New() router.POST("/pages", a.Create) router.GET("/pages", a.GetAll) router.GET("/pages/:id", a.Get) router.PUT("/pages/:id", a.Update) router.DELETE("/pages/:id", a.Delete) return router }
  27. package api // api/api.go type API struct { storage *storage.Storage

    server *http.Server } func NewAPI(storage *storage.Storage) *API { return &API{ storage: storage, } } func (a *API) Start(port string) error { a.server = &http.Server{ Addr: ":" + port, Handler: a.bootRouter(), } return a.server.ListenAndServe() } func (a *API) Shutdown() error { return a.server.Shutdown(context.Background()) } func (a *API) bootRouter() *httprouter.Router { router := httprouter.New() router.POST("/pages", a.Create) router.GET("/pages", a.GetAll) router.GET("/pages/:id", a.Get) router.PUT("/pages/:id", a.Update) router.DELETE("/pages/:id", a.Delete) return router }
  28. package api // api/api.go type API struct { storage *storage.Storage

    server *http.Server } func NewAPI(storage *storage.Storage) *API { return &API{ storage: storage, } } func (a *API) Start(port string) error { a.server = &http.Server{ Addr: ":" + port, Handler: a.bootRouter(), } return a.server.ListenAndServe() } func (a *API) Shutdown() error { return a.server.Shutdown(context.Background()) } func (a *API) bootRouter() *httprouter.Router { router := httprouter.New() router.POST("/pages", a.Create) router.GET("/pages", a.GetAll) router.GET("/pages/:id", a.Get) router.PUT("/pages/:id", a.Update) router.DELETE("/pages/:id", a.Delete) return router }
  29. package main // main.go func main() { apiPort := flag.String("port",

    "8080", "service port") flag.Parse() s := storage.NewStorage(redis.NewClient(&redis.Options{Addr: "localhost:6379"})) a := api.NewAPI(s) // shutdown gracefully go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs log.Println("performing shutdown...") if err := a.Shutdown(); err != nil { log.Printf("failed to shutdown server: %v", err) } }() log.Printf("service is ready to listen on port: %s", *apiPort) if err := a.Start(*apiPort); err != http.ErrServerClosed { log.Printf("server failed: %v", err) os.Exit(1) } }
  30. package main // main.go func main() { apiPort := flag.String("port",

    "8080", "service port") flag.Parse() s := storage.NewStorage(redis.NewClient(&redis.Options{Addr: "localhost:6379"})) a := api.NewAPI(s) // shutdown gracefully go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs log.Println("performing shutdown...") if err := a.Shutdown(); err != nil { log.Printf("failed to shutdown server: %v", err) } }() log.Printf("service is ready to listen on port: %s", *apiPort) if err := a.Start(*apiPort); err != http.ErrServerClosed { log.Printf("server failed: %v", err) os.Exit(1) } }
  31. package main // main.go func main() { apiPort := flag.String("port",

    "8080", "service port") flag.Parse() s := storage.NewStorage(redis.NewClient(&redis.Options{Addr: "localhost:6379"})) a := api.NewAPI(s) // shutdown gracefully go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs log.Println("performing shutdown...") if err := a.Shutdown(); err != nil { log.Printf("failed to shutdown server: %v", err) } }() log.Printf("service is ready to listen on port: %s", *apiPort) if err := a.Start(*apiPort); err != http.ErrServerClosed { log.Printf("server failed: %v", err) os.Exit(1) } }
  32. package main // main.go func main() { apiPort := flag.String("port",

    "8080", "service port") flag.Parse() s := storage.NewStorage(redis.NewClient(&redis.Options{Addr: "localhost:6379"})) a := api.NewAPI(s) // shutdown gracefully go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs log.Println("performing shutdown...") if err := a.Shutdown(); err != nil { log.Printf("failed to shutdown server: %v", err) } }() log.Printf("service is ready to listen on port: %s", *apiPort) if err := a.Start(*apiPort); err != http.ErrServerClosed { log.Printf("server failed: %v", err) os.Exit(1) } }
  33. package main // main.go func main() { apiPort := flag.String("port",

    "8080", "service port") flag.Parse() r := redis.NewClient(&redis.Options{Addr: "localhost:"+*apiPort}) s := storage.NewStorage(r) a := api.NewAPI(s) // shutdown gracefully go func() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) <-sigs log.Println("performing shutdown...") if err := a.Shutdown(); err != nil { log.Printf("failed to shutdown server: %v", err) } }() log.Printf("service is ready to listen on port: %s", *apiPort) if err := a.Start(*apiPort); err != http.ErrServerClosed { log.Printf("server failed: %v", err) os.Exit(1) } }
  34. package storage // storage/storage.go type Storage struct { redis *redis.Client

    } func NewStorage(redis *redis.Client) *Storage { return &Storage{ redis, } } type Page struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` }
  35. package storage // storage/storage.go type Storage struct { redis *redis.Client

    } func NewStorage(redis *redis.Client) *Storage { return &Storage{ redis, } } type Page struct { ID string `json:"id"` Title string `json:"title"` Description string `json:"description"` }
  36. func (s *Storage) GetPageByID(id string) (p *Page, err error) {

    b, err := s.redis.Get(dbKey + id).Bytes() if err != nil { err = fmt.Errorf("failed to get page %s: %v", id, err) return } err = json.Unmarshal(b, &p) if err != nil { err = fmt.Errorf("failed to unmarshal %s: %v", id, err) return } return }
  37. func (s *Storage) GetPageByID(id string) (p *Page, err error) {

    b, err := s.redis.Get(dbKey + id).Bytes() if err != nil { err = fmt.Errorf("failed to get page %s: %v", id, err) return } err = json.Unmarshal(b, &p) if err != nil { err = fmt.Errorf("failed to unmarshal %s: %v", id, err) return } return }
  38. func (s *Storage) GetPageByID(id string) (p *Page, err error) {

    b, err := s.redis.Get(dbKey + id).Bytes() if err != nil { err = fmt.Errorf("failed to get page %s: %v", id, err) return } err = json.Unmarshal(b, &p) if err != nil { err = fmt.Errorf("failed to unmarshal %s: %v", id, err) return } return }
  39. func (a *API) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    id := ps.ByName("id") var page storage.Page err := json.NewDecoder(r.Body).Decode(&page) if err != nil { log.Printf("failed to decode body: %v", err) write(w, 400, nil) return } err = a.storage.UpdatePageByID(id, &page) if err != nil { log.Printf("failed to update page: %v", err) write(w, 500, nil) return } write(w, 200, okResponse()) }
  40. func (a *API) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    id := ps.ByName("id") var page storage.Page err := json.NewDecoder(r.Body).Decode(&page) if err != nil { log.Printf("failed to decode body: %v", err) write(w, 400, nil) return } err = a.storage.UpdatePageByID(id, &page) if err != nil { log.Printf("failed to update page: %v", err) write(w, 500, nil) return } write(w, 200, okResponse()) } func okResponse() []byte { return []byte(`{"message":"Ok"}`) } func write(w http.ResponseWriter, statusCode int, body []byte) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) _, err := w.Write(body) if err != nil { log.Printf("failed to write: %v", err) } }
  41. func (a *API) Update(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    id := ps.ByName("id") var page storage.Page err := json.NewDecoder(r.Body).Decode(&page) if err != nil { log.Printf("failed to decode body: %v", err) write(w, 400, nil) return } err = a.storage.UpdatePageByID(id, &page) if err != nil { log.Printf("failed to update page: %v", err) write(w, 500, nil) return } write(w, 200, okResponse()) } func okResponse() []byte { return []byte(`{"message":"Ok"}`) } func write(w http.ResponseWriter, statusCode int, body []byte) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) _, err := w.Write(body) if err != nil { log.Printf("failed to write: %v", err) } }
  42. . !"" api # !"" api.go # $"" handlers.go !""

    storage # $"" storage.go $"" main.go
  43. . !"" api # !"" api.go # $"" handlers.go !""

    storage # $"" storage.go $"" main.go
  44. Summary (good) • Simple • One way of doing things

    (mostly) • Transparent • Explicit dependencies
  45. Simple: a line of code that does something very small

    and specific. Easy: a line of code that does a lot by calling a framework function causing thousands of lines of code to be executed. https://ilya-sher.org/2016/05/19/tips-for-beginning-systems-and-software-engineers/
  46. Simple: a line of code that does something very small

    and specific. https://ilya-sher.org/2016/05/19/tips-for-beginning-systems-and-software-engineers/
  47. • Go Proverbs by Rob Pike • 7 common mistakes

    in Go and when to avoid them by Steve Francia • Best Practices for Industrial Programming by Peter Bourgon • Advanced Testing with Go by Mitchell Hashimoto • Things in Go I Never Use by Mat Ryer • JustForFunc: Programming in Go by Francesc Campoy • Go Lift by John Cinnamond • Twelve Go Best Practices by Francesc Campoy