際際滷

際際滷Share a Scribd company logo
GET BETTER AT REFACTORING
stanly@odd-e.com
@stanlylau
INTRODUCTION
ABOUT ME
 Lives in Singapore
 Software development
coach / mentor
 Lead Agile Singapore
community
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String post(@ModelAttribute MailSendForm form, Model model) {
model.addAttribute("form", form);
return "send";
}
loc: 4
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String post(@ModelAttribute MailSendForm form, Model model) {
// TODO Validate Request
String address = form.getAddress();
boolean isMailAddress = Validator.isMailAddress(address);
if (!isMailAddress) {
model.addAttribute("form", form);
model.addAttribute("error", "error");
return "send";
}
String subject = form.getSubject();
boolean isSubject = Validator.isSubject(subject);
String body = form.getBody();
boolean isBody = Validator.isBody(body);
// TODO Mail Send
// TODO Show the page
model.addAttribute("form", form);
return "send";
}
loc: 14
@RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public String sendEmail(@ModelAttribute MailSendForm form, Model model) {
if (!Validator.isMailAddress(form.getAddress())) {
model.addAttribute("error", "error");
return "send";
}
if (!Validator.isSubject(form.getSubject())) {
model.addAttribute("error", "error");
return "send";
}
if (!Validator.isBody(form.getBody())) {
model.addAttribute("error", "error");
return "send";
}
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com",
form.getAddress(), form.getSubject(), form.getBody());
try {
mailService.send(mail);
} catch (Exception e) {
e.printStackTrace();
}
return "send";
}
loc: 17
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model)
{
if (result.hasErrors()) {
model.addAttribute("errorMessage", "error");
return "send";
}
String[] addresses = form.getAddress().split("s*;s*");
boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address));
if (isValid) {
model.addAttribute("errorMessage", "error");
return "send";
}
for (String address : addresses) {
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, form.getSubject(),
form.getBody());
try {
mailService.send(mail);
} catch (Exception e) {
model.addAttribute("errorMessage", "error");
return "send";
}
}
return "send";
}
loc: 18
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String sendEmail(@ModelAttribute MailSendForm form, Model model) {
if (!Validator.isSubject(form.getSubject())) {
model.addAttribute("errorMessage", "error");
return "send";
}
if (!Validator.isBody(form.getBody())) {
model.addAttribute("errorMessage", "error");
return "send";
}
String addressFromForm = form.getAddress();
if (StringUtils.isEmpty(addressFromForm)) {
model.addAttribute("errorMessage", "error");
return "send";
}
String[] addresses = addressFromForm.split(";");
for(String address : addresses) {
if (!Validator.isMailAddress(address)) {
model.addAttribute("errorMessage", "error");
return "send";
}
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, form.getSubject(),
form.getBody());
try {
mailService.send(mail);
} catch (Exception e) {
model.addAttribute("errorMessage", "error");
}
}
return "send";
}
loc: 23
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("errorMessage", "error");
return "send";
}
String[] addresses = form.getAddress().split(s*;s*");
boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address));
if (isValid) {
model.addAttribute("errorMessage", "error");
return "send";
}
try {
List<MailInfo> mailInfoList = new ArrayList<>();
String subject = form.getSubject();
for (String address : addresses) {
AddressItem addressItem = addressBookService.findByAddress(address);
if (addressItem != null) {
if (StringUtils.isEmpty(addressItem.getName()) && StringUtils.contains(subject, "$name")) {
throw new Exception("name attribute is empty!!");
}
subject = StringUtils.replace(subject, "$name", addressItem.getName());
}
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, subject, form.getBody());
mailInfoList.add(mail);
}
mailService.sendMultiple(mailInfoList);
} catch (Exception e) {
model.addAttribute("errorMessage", "error");
return "send";
}
return "send";
loc: 26
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("errorMessage", "error");
return "send";
}
String[] addresses = form.getAddress().split("s*;s*");
boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address));
if (isValid) {
model.addAttribute("errorMessage", "error");
return "send";
}
try {
List<MailInfo> mailInfoList = new ArrayList<>();
String subject = form.getSubject();
for (String address : addresses) {
AddressItem addressItem = addressBookService.findByAddress(address);
String replacedSubject = subject;
if (addressItem != null) {
if (StringUtils.isEmpty(addressItem.getName()) && StringUtils.contains(subject, "$name")) {
throw new Exception("name attribute is empty!!");
}
replacedSubject = StringUtils.replace(subject, "$name", addressItem.getName());
}else {
if (StringUtils.contains(subject, "$name")){
throw new Exception("email address is not registered");
}
}
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, replacedSubject, form.getBody());
mailInfoList.add(mail);
}
mailService.sendMultiple(mailInfoList);
} catch (Exception e) {
model.addAttribute("errorMessage", "error");
return "send";
}
return "send";
loc: 30
@RequestMapping(value = "/send", method = RequestMethod.POST)
public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("errorMessage", "error");
return "send";
}
String[] addresses = form.getAddress().split("s*;s*");
boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address));
if (isValid) {
model.addAttribute("errorMessage", "error");
return "send";
}
try {
List<MailInfo> mailInfoList = new ArrayList<>();
String subject = form.getSubject();
String body = form.getBody();
for (String address : addresses) {
String replacedSubject = subject;
String replacedBody = body;
if (StringUtils.contains(subject, "$name") || StringUtils.contains(body, "$name")) {
AddressItem addressItem = addressBookService.findByAddress(address);
if (addressItem == null) {
throw new Exception("email address is not registered");
}
if (StringUtils.isEmpty(addressItem.getName())) {
throw new Exception("name attribute is empty!!");
}
replacedSubject = StringUtils.replace(subject, "$name", addressItem.getName());
replacedBody = StringUtils.replace(body, "$name", addressItem.getName());
}
MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, replacedSubject, replacedBody);
mailInfoList.add(mail);
}
mailService.sendMultiple(mailInfoList);
} catch (Exception e) {
model.addAttribute("errorMessage", "error");
return "send";
}
return "send";
}
loc: 32
DEVELOPERS GET BETTER AT
COMPLEXITY AND SOMETIMES
THEY BELIEVE IT IS INEVITABLE.
EXPERIENCED PROGRAMMERS DO NOT
WRITE INFINITELY COMPLEX CODE;
THEY WRITE CODE THATS BLINDINGLY
SIMPLE
99 Bottles of OOP
GROWTH OF A DEVELOPER
THE NATURE OF SOFTWARE DEVELOPMENT
LEARNING THIS NATURE
AND DEALING WITH IT
HELPS TO INCREASE
FLEXIBILITY.
AMOUNT OF
BAD CODE
# BUGS
TIME SPENT
ON FIXING
BUGS
PANIC
QUICK HACKS
MOTIVATION OF
DEVELOPERS
REFACTORING
AMOUNT OF
CODE SMELLS
INDICATES
OPPORTUNITY FOR
IRONICALLY, DEVELOPERS
ARE TOLD TO DO MORE
WHAT CAN HELP?
SOFTWARE MAINTAINABILITY
REFACTORING
IS A DISCIPLINED TECHNIQUE FOR
RESTRUCTURING AN EXISTING BODY OF
CODE, ALTERING ITS INTERNAL
STRUCTURE WITHOUT CHANGING ITS
EXTERNAL BEHAVIOR.
MARTIN FOWLER
INTRODUCE
PARAMETER OBJECT
EXTRACT CLASS
INTRODUCE
LOCAL VARIABLE
RENAME
MOVE METHODEXTRACT METHOD
INLINE VARIABLE
REFACTORING
WHY? WHAT? HOW?
GET READY
FOR
CHANGE
DESIGN CODE
SMELLS
CODE SMELLS
A CODE SMELL IS A SURFACE
INDICATION THAT USUALLY
CORRESPONDS TO A DEEPER PROBLEM
IN THE SYSTEM
MARTIN FOWLER
DUPLICATED CODE
MAGIC NUMBER
FEATURE ENVY
PRIMITIVE OBSESSION
LONG METHOD
COMMENTS
DEMO
PERSONALISED EMAILER
WEB APP
Get better at Refactoring
RECEIVED (NORMAL)
TO: NON-CONTACT@GMAIL.COM
SUBJECT: SEASONS GREETING
TO: NON-CONTACT2@GMAIL.COM
SUBJECT: SEASONS GREETING
Get better at Refactoring
Get better at Refactoring
RECEIVED (TEMPLATE)
TO: STANLY@ODD-E.COM
SUBJECT: SEASONS GREETING STANLY
TO: AKI@ODD-E.COM
SUBJECT: SEASONS GREETING AKI
DESIGN DIRECTION
BRING OUT TEMPLATE
CONCEPT
HOW TO START
HOW TO START
ESSENTIAL SKILLS
 Explain the code with design principles
 Identify code smells
 Refactor either manually or through IDE
 Familiar with unit/integration tests
 Familiar with functionality behaviour youre changing
 Understand abstractions
TRY CODING DOJO SESSIONS
ON PRODUCTION CODE
PICK CODE SMELLS TO REMOVE
LEARN TO SMELL
THE CODE
CODE SMELLS
PRACTICE
REFACTORING KATA
https://github.com/stanlylau/tictactoe_csharp
https://github.com/stanlylau/refactoring-kata
https://github.com/emilybache/Tennis-Refactoring-Kata
https://github.com/emilybache/GildedRose-Refactoring-Kata
https://github.com/stanlylau/trivia-csharp
DESIGN PRINCIPLES
SINGLE RESPONSIBILITY
HI COHESION, LOW COUPLING
Get better at Refactoring
TEACH TO LEARN
1. CREATE CODE SMELLS EXERCISES
2. PRACTICE REMOVING THE SMELLS
3. INVITE PEOPLE TO REMOVE THE SMELLS
4. CODE REVIEW
5. DEMO
CRAFTSMANSHIP
REFACTORING IS
AN EXERCISE IN
KINDNESS
Kent Beck
THANK YOU
STANLY@ODD-E.COM

More Related Content

Get better at Refactoring

  • 1. GET BETTER AT REFACTORING stanly@odd-e.com @stanlylau
  • 2. INTRODUCTION ABOUT ME Lives in Singapore Software development coach / mentor Lead Agile Singapore community
  • 3. @RequestMapping(value = "/send", method = RequestMethod.POST) public String post(@ModelAttribute MailSendForm form, Model model) { model.addAttribute("form", form); return "send"; } loc: 4
  • 4. @RequestMapping(value = "/send", method = RequestMethod.POST) public String post(@ModelAttribute MailSendForm form, Model model) { // TODO Validate Request String address = form.getAddress(); boolean isMailAddress = Validator.isMailAddress(address); if (!isMailAddress) { model.addAttribute("form", form); model.addAttribute("error", "error"); return "send"; } String subject = form.getSubject(); boolean isSubject = Validator.isSubject(subject); String body = form.getBody(); boolean isBody = Validator.isBody(body); // TODO Mail Send // TODO Show the page model.addAttribute("form", form); return "send"; } loc: 14
  • 5. @RequestMapping(value = "/sendEmail", method = RequestMethod.POST) public String sendEmail(@ModelAttribute MailSendForm form, Model model) { if (!Validator.isMailAddress(form.getAddress())) { model.addAttribute("error", "error"); return "send"; } if (!Validator.isSubject(form.getSubject())) { model.addAttribute("error", "error"); return "send"; } if (!Validator.isBody(form.getBody())) { model.addAttribute("error", "error"); return "send"; } MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", form.getAddress(), form.getSubject(), form.getBody()); try { mailService.send(mail); } catch (Exception e) { e.printStackTrace(); } return "send"; } loc: 17
  • 6. @RequestMapping(value = "/send", method = RequestMethod.POST) public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) { if (result.hasErrors()) { model.addAttribute("errorMessage", "error"); return "send"; } String[] addresses = form.getAddress().split("s*;s*"); boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address)); if (isValid) { model.addAttribute("errorMessage", "error"); return "send"; } for (String address : addresses) { MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, form.getSubject(), form.getBody()); try { mailService.send(mail); } catch (Exception e) { model.addAttribute("errorMessage", "error"); return "send"; } } return "send"; } loc: 18
  • 7. @RequestMapping(value = "/send", method = RequestMethod.POST) public String sendEmail(@ModelAttribute MailSendForm form, Model model) { if (!Validator.isSubject(form.getSubject())) { model.addAttribute("errorMessage", "error"); return "send"; } if (!Validator.isBody(form.getBody())) { model.addAttribute("errorMessage", "error"); return "send"; } String addressFromForm = form.getAddress(); if (StringUtils.isEmpty(addressFromForm)) { model.addAttribute("errorMessage", "error"); return "send"; } String[] addresses = addressFromForm.split(";"); for(String address : addresses) { if (!Validator.isMailAddress(address)) { model.addAttribute("errorMessage", "error"); return "send"; } MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, form.getSubject(), form.getBody()); try { mailService.send(mail); } catch (Exception e) { model.addAttribute("errorMessage", "error"); } } return "send"; } loc: 23
  • 8. @RequestMapping(value = "/send", method = RequestMethod.POST) public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) { if (result.hasErrors()) { model.addAttribute("errorMessage", "error"); return "send"; } String[] addresses = form.getAddress().split(s*;s*"); boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address)); if (isValid) { model.addAttribute("errorMessage", "error"); return "send"; } try { List<MailInfo> mailInfoList = new ArrayList<>(); String subject = form.getSubject(); for (String address : addresses) { AddressItem addressItem = addressBookService.findByAddress(address); if (addressItem != null) { if (StringUtils.isEmpty(addressItem.getName()) && StringUtils.contains(subject, "$name")) { throw new Exception("name attribute is empty!!"); } subject = StringUtils.replace(subject, "$name", addressItem.getName()); } MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, subject, form.getBody()); mailInfoList.add(mail); } mailService.sendMultiple(mailInfoList); } catch (Exception e) { model.addAttribute("errorMessage", "error"); return "send"; } return "send"; loc: 26
  • 9. @RequestMapping(value = "/send", method = RequestMethod.POST) public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) { if (result.hasErrors()) { model.addAttribute("errorMessage", "error"); return "send"; } String[] addresses = form.getAddress().split("s*;s*"); boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address)); if (isValid) { model.addAttribute("errorMessage", "error"); return "send"; } try { List<MailInfo> mailInfoList = new ArrayList<>(); String subject = form.getSubject(); for (String address : addresses) { AddressItem addressItem = addressBookService.findByAddress(address); String replacedSubject = subject; if (addressItem != null) { if (StringUtils.isEmpty(addressItem.getName()) && StringUtils.contains(subject, "$name")) { throw new Exception("name attribute is empty!!"); } replacedSubject = StringUtils.replace(subject, "$name", addressItem.getName()); }else { if (StringUtils.contains(subject, "$name")){ throw new Exception("email address is not registered"); } } MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, replacedSubject, form.getBody()); mailInfoList.add(mail); } mailService.sendMultiple(mailInfoList); } catch (Exception e) { model.addAttribute("errorMessage", "error"); return "send"; } return "send"; loc: 30
  • 10. @RequestMapping(value = "/send", method = RequestMethod.POST) public String sendEmail(@Valid @ModelAttribute MailSendForm form, BindingResult result, Model model) { if (result.hasErrors()) { model.addAttribute("errorMessage", "error"); return "send"; } String[] addresses = form.getAddress().split("s*;s*"); boolean isValid = Stream.of(addresses).anyMatch(address -> !Validator.isMailAddress(address)); if (isValid) { model.addAttribute("errorMessage", "error"); return "send"; } try { List<MailInfo> mailInfoList = new ArrayList<>(); String subject = form.getSubject(); String body = form.getBody(); for (String address : addresses) { String replacedSubject = subject; String replacedBody = body; if (StringUtils.contains(subject, "$name") || StringUtils.contains(body, "$name")) { AddressItem addressItem = addressBookService.findByAddress(address); if (addressItem == null) { throw new Exception("email address is not registered"); } if (StringUtils.isEmpty(addressItem.getName())) { throw new Exception("name attribute is empty!!"); } replacedSubject = StringUtils.replace(subject, "$name", addressItem.getName()); replacedBody = StringUtils.replace(body, "$name", addressItem.getName()); } MailInfo mail = new MailInfo("gadget.mailsender@gmail.com", address, replacedSubject, replacedBody); mailInfoList.add(mail); } mailService.sendMultiple(mailInfoList); } catch (Exception e) { model.addAttribute("errorMessage", "error"); return "send"; } return "send"; } loc: 32
  • 11. DEVELOPERS GET BETTER AT COMPLEXITY AND SOMETIMES THEY BELIEVE IT IS INEVITABLE.
  • 12. EXPERIENCED PROGRAMMERS DO NOT WRITE INFINITELY COMPLEX CODE; THEY WRITE CODE THATS BLINDINGLY SIMPLE 99 Bottles of OOP GROWTH OF A DEVELOPER
  • 13. THE NATURE OF SOFTWARE DEVELOPMENT LEARNING THIS NATURE AND DEALING WITH IT HELPS TO INCREASE FLEXIBILITY. AMOUNT OF BAD CODE # BUGS TIME SPENT ON FIXING BUGS PANIC QUICK HACKS MOTIVATION OF DEVELOPERS REFACTORING AMOUNT OF CODE SMELLS INDICATES OPPORTUNITY FOR
  • 15. WHAT CAN HELP? SOFTWARE MAINTAINABILITY
  • 16. REFACTORING IS A DISCIPLINED TECHNIQUE FOR RESTRUCTURING AN EXISTING BODY OF CODE, ALTERING ITS INTERNAL STRUCTURE WITHOUT CHANGING ITS EXTERNAL BEHAVIOR. MARTIN FOWLER INTRODUCE PARAMETER OBJECT EXTRACT CLASS INTRODUCE LOCAL VARIABLE RENAME MOVE METHODEXTRACT METHOD INLINE VARIABLE
  • 17. REFACTORING WHY? WHAT? HOW? GET READY FOR CHANGE DESIGN CODE SMELLS
  • 18. CODE SMELLS A CODE SMELL IS A SURFACE INDICATION THAT USUALLY CORRESPONDS TO A DEEPER PROBLEM IN THE SYSTEM MARTIN FOWLER DUPLICATED CODE MAGIC NUMBER FEATURE ENVY PRIMITIVE OBSESSION LONG METHOD COMMENTS
  • 21. RECEIVED (NORMAL) TO: NON-CONTACT@GMAIL.COM SUBJECT: SEASONS GREETING TO: NON-CONTACT2@GMAIL.COM SUBJECT: SEASONS GREETING
  • 24. RECEIVED (TEMPLATE) TO: STANLY@ODD-E.COM SUBJECT: SEASONS GREETING STANLY TO: AKI@ODD-E.COM SUBJECT: SEASONS GREETING AKI
  • 25. DESIGN DIRECTION BRING OUT TEMPLATE CONCEPT
  • 27. HOW TO START ESSENTIAL SKILLS Explain the code with design principles Identify code smells Refactor either manually or through IDE Familiar with unit/integration tests Familiar with functionality behaviour youre changing Understand abstractions
  • 28. TRY CODING DOJO SESSIONS ON PRODUCTION CODE PICK CODE SMELLS TO REMOVE
  • 29. LEARN TO SMELL THE CODE CODE SMELLS
  • 33. TEACH TO LEARN 1. CREATE CODE SMELLS EXERCISES 2. PRACTICE REMOVING THE SMELLS 3. INVITE PEOPLE TO REMOVE THE SMELLS 4. CODE REVIEW 5. DEMO
  • 35. REFACTORING IS AN EXERCISE IN KINDNESS Kent Beck