0
Be#er	  So(ware  An	  introduc2on	  to	  good	  code    	  	  Giordano	  Scalzo,	  06/05/2009
• Why code matters (10 min)    • Disclaimer    • Good and Bad Code    • The Broken Window Theory    • The Grand Redesign i...
Disclaimer
I’m not here to preach
Ward Cunningham40 years experienceWiki inventorExtreme Programming pioneerDirector of Eclipse
Kent BeckExtreme Programming inventorJUnit inventorTdd inventor
Robert C. MartinUncle Bob40 years experienceExtreme Programming pioneer
What is Good Code?
Bad CodeCan make scared!
while ((!found) && (pos < (fileContent.Length - 6))){    byteData = new byte[6];    Array.Copy(fileContent, pos, byteData,...
Theory of Broken Window
Big Redesign in the Sky
Netscape rewrote Netscape 4.0 and released it   after three years as Netscape 6.0
Borland      rewrote dBase and Quattro Pro
Microsoft            rewrote Word
The Boy Scout RuleLeave the campground cleaner than you found it
OOP Principles
S.o.l.i.d. Principles Solid Principles
Single Responsibility PrincipleOnly one reason to changeRobustnessFocus
public class PrintServerImpl extends ServiceAdvanced                             implements PrintServer, JobListener{    p...
public class PrinterServerJob {    public synchronized String createJob(Object data) { //...    }    public int getStatus(...
public class PrintServerImpl extends ServiceAdvanced             implements PrintServer, JobListener{   private Map<String...
OpenClose Principleopen for extensionclose for modificationabstraction
public static final int TYPE_UNDEFINED = 0;public static final int TYPE_LEGAL_AG = 1;public static final int TYPE_LEGAL_PG...
public interface ArchiveType {          public boolean isOk(String buildType);          public String getPrefix();}public ...
public class LegalPg implements ArchiveType {          public boolean isOk(String buildType){                    return bu...
//...archTypes.put(0,   new   Undefined());archTypes.put(1,   new   LegalAg());archTypes.put(2,   new   LegalPg());archTyp...
Liskov Substitution PrincipleIf for each object o1 of type S there is an object o2 of type T suchthat for all programs P d...
Liskov Substitution PrincipleSubtypes must be substitutable for their base typesInheritance and polymorphism
public class Rectangle {    protected int _width;    protected int _height;    public int Width{        get { return _widt...
[TestFixture]public class RectangleTests{    private void CheckAreaOfRectangle(Rectangle r){        r.SetWidth(5);        ...
public class Rectangle {    protected int _width;    protected int _height;    public int Width{        get { return _widt...
Interface Segregation PrincipleDon’t be force to implement unused methodsAvoid “Fat Interfaces”High cohesion - better unde...
public interface CartographyListener {    public void poisChanged(Locations pois);    public void cellsChanged(Locations c...
public abstract class Layer implements CartographyListener, DrawingInterface {    /* Methods from CartographyListener inte...
public class CartographyUI extends JPanel {    public void addCartographyListener(CartographyListener listener) {        i...
public interface CartographyListener {}public interface CellListener extends CartographyListener {          public void ce...
public class CartographyUI extends JPanel {    public void setCelles(Locations celles) {                    Iterator liste...
Dependency Injection PrincipleInversion of ControlHollywood Principle: "dont call us, we will call you"Dip <> Spring
private boolean retriveCallSideInfo(String Side) {    //...    DeterminationMethod = getXmlOption("Method"); // specify wh...
public abstract class CallSideDeterminator {    public abstract boolean findItem();    //...}public class CallSideDetermin...
public class AsExecFindCallSide extends AsCommandExec {    HashMap<String, CallSideDeterminator> strategies=              ...
The Others Principles
Reuse Release Equivalency PrincipleThe granule of reuse is the granule of releaseA package can be considered unit of distr...
Common Closure PrincipleMaintainability is more important than reusabilityIf code must change, all changes should bein the...
Common Reuse PrincipleThe classes in a package are reused togetherIf you reuse one of the classes in a package, you reuse ...
Acyclic Dependencies PrincipleAvoid cyclic dependenciesNightmare to compileNightmare to deploy
Least Astonishment PrincipleThe result of some operation should be obvious, consistent, predictableOccam’s Razor: The simp...
int multiply(int a, int b) {  return a + b;}int write_to_file(const char* filename, const char* text){   printf("[%s]n", t...
Law of DemeterEncapsulationAn object A can request a service (call a method) ofan object instance B, but object A cannot“r...
Inventory.SalesInfo.Items.Count = 2500;Room.getPlan().canCustomizeWindow()Room.getPlan().canSelectStyle()Room.getPlan().ha...
Inventory.SetSalesItemsCount(2500);Room.canCustomizeWindow()Room.canSelectStyle()Room.hasCeilingFan()
Smells and Heuristics
Comments
Obsolete CommentsOld comments that have lost their meaning
//***********************************************************************************//! Initalize procedure/*!*    This m...
public void initLog(log4net.ILog inLog){    log = inLog;    log.Debug("============ INIT Module " + getModuleName() + " ==...
Redundant CommentsVersion History
/**  *  ** 03 Oct 2005 - AB - Added the isSameCIDinSameLiuID() function to avoiddifferent CID in the same LIU (Ticket#2564...
svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java
Redundant CommentsRepeating the variable name or condition in the comment
//// list on a file all the Extractors extensions//if ( param == "-LISTEXTRACTORS" )//...
Redundant CommentsRepeating the called method name in a comment after the call
int abs = x.abs(); // Get the absolute value of xint x = point.getX(); // Get the value of x
int abs = x.abs();int x = point.getX();
Redundant CommentsCommented Out Code
pos = pos + 9;//byteData[0] = (byte)fstr.ReadByte();if (byteData[0] == 0x65)      // e{    //byteData[1] = (byte)fstr.Read...
pos = pos + 9;if (byteData[0] == 0x65)      // e{    if (byteData[1] == 0x6E)      // n    {        if (byteData[2] == 0x6...
Redundant CommentsComments related to structure
////     Upgrade section//if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" ){     Int...
manageUpdate(param);
Functions
FunctionsLong Methods
protected void build(DynamicView view, File baseTemplate, File outTemplate)throws ArchiverException, WrEntsException {    ...
//Sorted lines        for (int j = 0; j < colNames.size(); j++) {            line = (String)lines.get(colNames.get(j));   ...
else {                     if (record.toUpperCase().indexOf("{START_SORTED_RECS}") >= 0) {                         sortedB...
FunctionsDeeply nested methods
//From TE-PDF exctractorif (str_byteData.Contains("s")){    posE_byteData = str_byteData.IndexOf("s");    pos = pos + (pos...
else                  {                         if (byteData[4] == 0x73)                         {                        ...
else           {                  if (byteData[1] == 0x73)                  {                       pos = pos - 5;        ...
FunctionsDifferent abstraction levelComposed methodDivide your program into methods thatperform one identifiable task
public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {   ////...
public void service(HttpServletRequest request, HttpServletResponse response)        throws ServletException, IOException ...
private boolean downloadByName(HttpServletRequest request) {    boolean getName = "Y".equals(request.getParameter("GETNAME...
private int part(HttpServletRequest request) {    String partNum = request.getParameter("PART");    int part = 1;    if (p...
General
DuplicationDry“Once, and only once”Duplication is a missed opportunity forabstraction
Dead CodeCode that isn’t executedif statement that checksfor a condition that can’t happenPrivate method never calledswitc...
Vertical SeparationVariables and function should be defined closeto where they are usedLocal variablesshould be declared j...
public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {   ////...
public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {   ////...
ClutterDefault constructor without implementationVariables not usedFunctions never calledUseless commentsKill’em all!
Selector ArgumentsAvoid boolean argumentsBreaks SRPSplit method in two methods
getLocalPrintService(jobId, data, false);public static PrintServiceInterface getLocalPrintService(                        ...
public static PrintServiceInterface getEmbeddedLocalPrintService(                                    String jobId, IRawDat...
Explanatory VarsAvoid ‘artistic’ programmingBreak the calculations up
Matcher match = headerPattern.matcher(line);if(match.find())    headers.put(match.group(1), match.group(2));
Matcher match = headerPattern.matcher(line);if(match.find()){    String key = match.group(1);    String value = match.grou...
Magic NumbersReplace Magic Numbers with Named Constants
m_tx1.setPageHeight(16837);m_tx1.setPageWidth(11906);m_tx1.setPageMarginB((int)(0.35   *   1440));m_tx1.setPageMarginL((in...
final static double PAGE_HEIGHT = 16837;final static double PAGE_WIDTH = 11906;final static int MARGIN = (int)(0.35 * 1440...
Negative ConditionalsNegatives is harder to understand than positivesAvoid negatives Conditionals
if (!buffer.shouldNotCompact()) {    //...}if(!envWarrantsEnabled)    sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "...
if (buffer.shouldCompact()) {    //...}if(envWarrantsDisabled)    sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV"...
Names
Descriptive NamesChoice descriptive namesReevaluate the appropriateness of the names
public int x() {    int q = 0;    int z = 0;    for (int kk = 0; kk < 10; kk++) {        if (l[z] == 10) {            q +=...
public int score() {    int score = 0;    int frame = 0;    for (int frameNumber = 0; frameNumber < 10; frameNumber++) {  ...
Unambiguous NamesChoose names that make the workings of afunction or variable unambiguous
public boolean stateMachine(int smStatus) {    //...}public boolean doAction() {    //...}
public boolean moveStateMachineToStatus(int smStatus) {    //...}public boolean doNextStepInProviderTechnique() {    //...}
Avoid EncodingsNames should not be encoded with type orscope informationHungarian Notation as obsolete legacy
protected   int         m_FilesCount;     //   Numero files contenuti nel jobprotected   int         m_VolumesCount;   // ...
protected   int         filesCount;     //   Numero files contenuti nel jobprotected   int         volumesCount;   //   Nu...
Describe Side EffectsNames should describe everything that afunction, variable, or class is or does
public DiskWriterJobs getJobs() {    if (m_Jobs == null) {        m_Jobs = new DiskWriterJobs(this);    }    return m_Jobs;}
public DiskWriterJobs createOrReturnJobs() {    if (m_Jobs == null) {        m_Jobs = new DiskWriterJobs(this);    }    re...
Tests
Insufficient TestsHow many tests? A test suite should test everything that could possibly breakUse a Coverage Tool:     Ja...
Don’t Skip Trivial TestsThey are better than documentationEasy today, maybe hard tomorrow
Exhaustively Test Near BugBugs tend to congregate
Tests Should Be FastIf slow, launched less frequentelyIf launched less frequentely, big change between launches and diffic...
Conclusions?
Readable Code
Test
Avoid Duplication
Readable Code        Test    Avoid Duplication
Q&A?
http://creativecommons.org/licenses/by-nc-sa/3.0/
Better Software: introduction to good code
Better Software: introduction to good code
Better Software: introduction to good code
Better Software: introduction to good code
Upcoming SlideShare
Loading in...5
×

Better Software: introduction to good code

2,688

Published on

Published in: Technology

Transcript of "Better Software: introduction to good code"

  1. 1. Be#er  So(ware An  introduc2on  to  good  code    Giordano  Scalzo,  06/05/2009
  2. 2. • Why code matters (10 min) • Disclaimer • Good and Bad Code • The Broken Window Theory • The Grand Redesign in the Sky • The Boy Scout Rule• OOP Patterns and Principles (30 min) • SOLID Principles • The Others Principles• Good code: Smells and heuristics (30 min) • Comments • Functions • General • Names • Test• Conclusion (5 min)• Q&A? (15 min)
  3. 3. Disclaimer
  4. 4. I’m not here to preach
  5. 5. Ward Cunningham40 years experienceWiki inventorExtreme Programming pioneerDirector of Eclipse
  6. 6. Kent BeckExtreme Programming inventorJUnit inventorTdd inventor
  7. 7. Robert C. MartinUncle Bob40 years experienceExtreme Programming pioneer
  8. 8. What is Good Code?
  9. 9. Bad CodeCan make scared!
  10. 10. while ((!found) && (pos < (fileContent.Length - 6))){ byteData = new byte[6]; Array.Copy(fileContent, pos, byteData, 0, 6); pos = pos + 6; str_byteData = enc.GetString(byteData); if (str_byteData.Contains("s")) { posE_byteData = str_byteData.IndexOf("s"); pos = pos + (posE_byteData - 6); Array.Copy(fileContent, pos, byteData, 0, 6); pos = pos + 6; if (byteData[0] == 0x73) // s { if (byteData[1] == 0x74) // t { if (byteData[2] == 0x72) // r { if (byteData[3] == 0x65) // e { if (byteData[4] == 0x61) // a { if (byteData[5] == 0x6D) // m { found = true; break; } else { if (byteData[5] == 0x73) { pos = pos - 1; } } }
  11. 11. Theory of Broken Window
  12. 12. Big Redesign in the Sky
  13. 13. Netscape rewrote Netscape 4.0 and released it after three years as Netscape 6.0
  14. 14. Borland rewrote dBase and Quattro Pro
  15. 15. Microsoft rewrote Word
  16. 16. The Boy Scout RuleLeave the campground cleaner than you found it
  17. 17. OOP Principles
  18. 18. S.o.l.i.d. Principles Solid Principles
  19. 19. Single Responsibility PrincipleOnly one reason to changeRobustnessFocus
  20. 20. public class PrintServerImpl extends ServiceAdvanced implements PrintServer, JobListener{ public synchronized String createJob(Object data) { //... } public int getStatus(String jobId) { //... } public void print(String jobId, int startPage, int endPage) { //... } public byte[] getPreview(String jobId, int pageNum) { //... } public IRawData getData(String jobId) { //... } public void abortAction(String jobId) { //... } public Vector getPrinterList() { //... } public synchronized void setPrinterList(Vector printerList) { //... } public void statusChanged(JobEvent jobEvent) { //... } public void pageComputed(JobEvent jobEvent) { //... } // ...}
  21. 21. public class PrinterServerJob { public synchronized String createJob(Object data) { //... } public int getStatus() { //... } public void addDataToJob() { //... } public void print(){ //... } public void print(int startPage, int endPage){ //... } public byte[] getPreview(int pageNum){ //... } // ...}public class PrinterList { public Vector getPrinterList(){ //... } public synchronized void setPrinterList(Vector printerList){ //... }}public class JobEventListener{ public void statusChanged(JobEvent jobEvent){ //... } public void pageComputed(JobEvent jobEvent){ //... }}
  22. 22. public class PrintServerImpl extends ServiceAdvanced implements PrintServer, JobListener{ private Map<String, PrinterServerJob> printerServerJob; private PrinterList printerList; private JobEventListener jobEventListener; public PrintServerJob getJob(String jobId) { return printerServerJob.get(jobId); } public Vector getPrinterList(){ return printerList.getPrinterList(); } public void setPrinterList(Vector printerList){ return printerList.setPrinterList(printerList); } public void statusChanged(JobEvent jobEvent){ jobEventListener.statusChanged(jobEvent); } public void pageComputed(JobEvent jobEvent){ jobEventListener.pageComputed(jobEvent); } //...}
  23. 23. OpenClose Principleopen for extensionclose for modificationabstraction
  24. 24. public static final int TYPE_UNDEFINED = 0;public static final int TYPE_LEGAL_AG = 1;public static final int TYPE_LEGAL_PG = 2;public static final int TYPE_TEMPORARY = 3;//...boolean ok = false;String buildType = m_cdInfo.buildType.toUpperCase();String prefix = "";switch (archType) {case TYPE_LEGAL_AG: if (buildType.equals("LEGAL_AG") || buildType.equals("LEGAL_AGPG")){ ok = true; } break;case TYPE_LEGAL_PG: if (buildType.equals("LEGAL_PG") || buildType.equals("LEGAL_AGPG")){ ok = true; } break;case TYPE_TEMPORARY: if (buildType.equals("TEMPORARY") || buildType.equals("PRV")){ ok = true; } prefix = "AP "; break;}if (!ok) { BurnerHelper.showError(...);}
  25. 25. public interface ArchiveType { public boolean isOk(String buildType); public String getPrefix();}public class Undefined implements ArchiveType { public boolean isOk(String buildType){ return false; } public String getPrefix() { return ""; }}public class LegalAg implements ArchiveType { public boolean isOk(String buildType){ return buildType.equals("LEGAL_AG") || buildType.equals("LEGAL_AGPG") } public String getPrefix() { return ""; }}
  26. 26. public class LegalPg implements ArchiveType { public boolean isOk(String buildType){ return buildType.equals("LEGAL_PG") || buildType.equals("LEGAL_AGPG") } public String getPrefix() { return ""; }}public class Temporary implements ArchiveType { public boolean isOk(String buildType){ return buildType.equals("TEMPORARY") || buildType.equals("PRV") } public String getPrefix() { return "AP"; }}
  27. 27. //...archTypes.put(0, new Undefined());archTypes.put(1, new LegalAg());archTypes.put(2, new LegalPg());archTypes.put(3, new Temporary());//...String buildType = m_cdInfo.buildType.toUpperCase();boolean ok = archTypes.get(archType).isOk(buildType);String prefix = archTypes.get(archType).getPrefix();if (!ok) { BurnerHelper.showError(...);}//...
  28. 28. Liskov Substitution PrincipleIf for each object o1 of type S there is an object o2 of type T suchthat for all programs P defined in terms of T, the behavior of P isunchanged when o1 is substituted for o2 then S is a subtype of T
  29. 29. Liskov Substitution PrincipleSubtypes must be substitutable for their base typesInheritance and polymorphism
  30. 30. public class Rectangle { protected int _width; protected int _height; public int Width{ get { return _width; } } public int Height{ get { return _height; } } public virtual void SetWidth(int width){ _width = width; } public virtual void SetHeight(int height){ _height = height; }}public class Square: Rectangle { public override void SetWidth(int width){ _width = width; _height = width; } public override void SetHeight(int height){ _height = height; _width = height; }}
  31. 31. [TestFixture]public class RectangleTests{ private void CheckAreaOfRectangle(Rectangle r){ r.SetWidth(5); r.SetHeight(2); Assert.IsEqual(r.Width * r.Height,10); } [Test] public void PassingTest(){ Rectangle r = new Rectangle(); CheckAreaOfRectangle(r); } [Test] public void FailingTest(){ Rectangle r = new Square(); CheckAreaOfRectangle(r); }}
  32. 32. public class Rectangle { protected int _width; protected int _height; public int Width{ get { return _width; } } public int Height{ get { return _height; } } public virtual void SetWidth(int width){ _width = width; } public virtual void SetHeight(int height){ _height = height; }}public class Square { protected int _side; public int Side{ get { return _side; } } public void SetSide(int side){ _side = side; }}
  33. 33. Interface Segregation PrincipleDon’t be force to implement unused methodsAvoid “Fat Interfaces”High cohesion - better understandability, robustnessLow coupling - better maintainability, high resistance to changes
  34. 34. public interface CartographyListener { public void poisChanged(Locations pois); public void cellsChanged(Locations cells); public void mapChanged(ImageIcon map); public void updateZoomLevel(int level); public void updateGeoArea(GeoArea ga); public void updateGridPosition(Point2D.Double gridPosition); public void updateMousePosition(Point position);}public class CellLayer extends Layer { /* Methods from CartographyListener interface */ public void cellsChanged(Locations cells) { setLocations(cells); } //.....}public class PoiLayer extends Layer { /* Methods from CartographyListener interface */ public void poisChanged(Locations locations) { setLocations(locations); } //.....}
  35. 35. public abstract class Layer implements CartographyListener, DrawingInterface { /* Methods from CartographyListener interface */ // Metto qui unimplementazione vuota (una sorta di adapter) così // non sono costretta a sovrascrivere i metodi in tutte le specializzazioni. public void poisChanged(Locations pois) {} public void cellsChanged(Locations cells) {} public void mapChanged(ImageIcon map) {} public void updateZoomLevel(int level) { m_zoomLevel = level; } public void updateGeoArea(GeoArea ga) { m_geoArea = ga; } public void updateGridPosition(Point2D.Double gridPosition) {} public void updateMousePosition(Point position) {} /* End of methods from CartographyListener interface *///.....}
  36. 36. public class CartographyUI extends JPanel { public void addCartographyListener(CartographyListener listener) { if (m_listeners == null) { m_listeners = new ArrayList(); } m_listeners.add(listener); } public void setCelles(Locations celles) { Iterator listeners = m_listeners.iterator(); while (listeners.hasNext()) { CartographyListener listener = (CartographyListener)listeners.next(); listener.cellsChanged(celles); } updateViewFromLocations(); } public void setPois(Locations pois) { Iterator listeners = m_listeners.iterator(); while (listeners.hasNext()) { CartographyListener listener = (CartographyListener)listeners.next(); listener.poisChanged(pois); } updateViewFromLocations(); } //.....}
  37. 37. public interface CartographyListener {}public interface CellListener extends CartographyListener { public void cellsChanged(Locations cells);}public interface PoiListener extends CartographyListener { public void poisChanged(Locations locations);}
  38. 38. public class CartographyUI extends JPanel { public void setCelles(Locations celles) { Iterator listeners = m_listeners.iterator(); while (listeners.hasNext()) { CellListener listener = (CellListener)listeners.next(); listener.cellsChanged(celles); } updateViewFromLocations(); } public void setPois(Locations pois) { Iterator listeners = m_listeners.iterator(); while (listeners.hasNext()) { PoiListener listener = (PoiListener)listeners.next(); listener.poisChanged(pois); } updateViewFromLocations(); } public void addCartographyListener(CartographyListener listener) { Class<?> c = genericObj.getClass(); Class<?> interfaces[] = c.getInterfaces(); for (Class<?> implementedIntf : interfaces) { if (implementedIntf.getName().equals( urmetcns.mito.ui.cartography.ui_components.PoiListener")) poiListeners.add((PoiListener)listener); if (implementedIntf.getName().equals( "urmetcns.mito.ui.cartography.ui_components.CellListener")) cellListeners.add((CellListener)listener); // ... } } // ...}
  39. 39. Dependency Injection PrincipleInversion of ControlHollywood Principle: "dont call us, we will call you"Dip <> Spring
  40. 40. private boolean retriveCallSideInfo(String Side) { //... DeterminationMethod = getXmlOption("Method"); // specify which method to be used //... if (DeterminationMethod.equals("PhoneMatch")) { //... } if (DeterminationMethod.equals("Exist")) { //Query to che the existence of a specified dictionary element sqlString= "SELECT count(*) AS Cnt FROM iri_dictionary " " WHERE iri_id = ? and key_dictionary = ?"; pstmt = (OraclePreparedStatement)assocInfo.conn.prepareStatement(sqlString); pstmt.setLong(1,assocInfo.myIRIId); pstmt.setString(2,Dictionary); } if (DeterminationMethod.equals("Compare")) { //... } if (DeterminationMethod.equals("InOnly")) { //Query alwais true for the //provider Telecom Internazionale sqlString= "SELECT 1 As Cnt FROM Dual"; //... } //... //return true if the info has been found return (itemFound == 1);
  41. 41. public abstract class CallSideDeterminator { public abstract boolean findItem(); //...}public class CallSideDeterminatorCompare extends CallSideDeterminator { @Override public boolean findItem(){ //... }}public class CallSideDeterminatorDFDM extends CallSideDeterminator { @Override public boolean findItem(){ //... }}public class CallSideDeterminatorPhoneMatch extends CallSideDeterminator { @Override public boolean findItem(){ //... }}
  42. 42. public class AsExecFindCallSide extends AsCommandExec { HashMap<String, CallSideDeterminator> strategies= new HashMap<String, CallSideDeterminator>(); private void init() { strategies= new HashMap<String, CallSideDeterminator>(); strategies.put("PhoneMatch", new CallSideDeterminatorPhoneMatch()); strategies.put("Compare", new CallSideDeterminatorCompare()); strategies.put("DFDM", new CallSideDeterminatorDFDM()); //... } protected boolean retrieveCallSideInfo(String side) { CallSideDeterminator determinator = null; if ((determinator = strategies.get( getXmlOption("Method"))) != null) { determinator.initDeterminator(assocInfo, side); if(determinator.findItem()) { return determinator.storeCIN(side); } } return false; } //...}
  43. 43. The Others Principles
  44. 44. Reuse Release Equivalency PrincipleThe granule of reuse is the granule of releaseA package can be considered unit of distributionA release should have a version numberBlack-box, package that is to be used but not changed
  45. 45. Common Closure PrincipleMaintainability is more important than reusabilityIf code must change, all changes should bein the same packageCommon changing classes, should be in the same package
  46. 46. Common Reuse PrincipleThe classes in a package are reused togetherIf you reuse one of the classes in a package, you reuse them all
  47. 47. Acyclic Dependencies PrincipleAvoid cyclic dependenciesNightmare to compileNightmare to deploy
  48. 48. Least Astonishment PrincipleThe result of some operation should be obvious, consistent, predictableOccam’s Razor: The simplest answer is usually the correct answer
  49. 49. int multiply(int a, int b) { return a + b;}int write_to_file(const char* filename, const char* text){ printf("[%s]n", text); /* Note that filename is unused */ }
  50. 50. Law of DemeterEncapsulationAn object A can request a service (call a method) ofan object instance B, but object A cannot“reach through” object B to access yetanother object, C, to request its servicesAn object should avoid invoking methods ofa member object returned by another method“Don’t talk to stranger"“Use only one dot"
  51. 51. Inventory.SalesInfo.Items.Count = 2500;Room.getPlan().canCustomizeWindow()Room.getPlan().canSelectStyle()Room.getPlan().hasCeilingFan()
  52. 52. Inventory.SetSalesItemsCount(2500);Room.canCustomizeWindow()Room.canSelectStyle()Room.hasCeilingFan()
  53. 53. Smells and Heuristics
  54. 54. Comments
  55. 55. Obsolete CommentsOld comments that have lost their meaning
  56. 56. //***********************************************************************************//! Initalize procedure/*!* This methos is called from the main in order to initialize all the thinks<br>* that the plugin need.** param inLog log that the plugin can use for its own purpose* return true = all ok false = intialization failed*///***********************************************************************************public bool init(log4net.ILog inLog){ this.log = inLog; //log.Debug("======================================================="); log.Debug("============ INIT Module " + getModuleName() + " ========="); return true;}
  57. 57. public void initLog(log4net.ILog inLog){ log = inLog; log.Debug("============ INIT Module " + getModuleName() + " =========");}
  58. 58. Redundant CommentsVersion History
  59. 59. /** * ** 03 Oct 2005 - AB - Added the isSameCIDinSameLiuID() function to avoiddifferent CID in the same LIU (Ticket#2564) * 09 Sep 2005 - AB - fixed the retriveCallSideInfo() for the PhoneMatchmethod (Ticket#2381) * 06 Sep 2005 - AB - Fixed the SearchProviderDate() to properly workwith the DATA association technique * 01 Sep 2005 - AB - Added the dupval index exception handling insaveInHiddenJournal() function * 27 Jul 2005 - AB - changed the isInformationInDb() to avoid exitingwith assocInfo.lemfList == null * 27 Jul 2005 - AB - removed the updateJournal() function because notneeded * 26 Jul 2005 - AB - Now mergeJournal() saves a copy of the two lius tibe merged in the hidden_journal table * 26 Jul 2005 - AB - Added the saveInHiddenJournal() function to enhancethe mergeJournal() function * 05 Jul 2005 - AB - Changed the retriveCallSideInfo queries to selectthe correct liu_id in every situation.… * 23 Mar 2005 - AB - Added the ORA-00001 error handling in theAddIRI2Journal * 9 Mar 2005 - AB - moved the queryExec body function to a genericqueryExec function in the IRITools * 11 May 2004 - AB - Started **/
  60. 60. svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java
  61. 61. Redundant CommentsRepeating the variable name or condition in the comment
  62. 62. //// list on a file all the Extractors extensions//if ( param == "-LISTEXTRACTORS" )//...
  63. 63. Redundant CommentsRepeating the called method name in a comment after the call
  64. 64. int abs = x.abs(); // Get the absolute value of xint x = point.getX(); // Get the value of x
  65. 65. int abs = x.abs();int x = point.getX();
  66. 66. Redundant CommentsCommented Out Code
  67. 67. pos = pos + 9;//byteData[0] = (byte)fstr.ReadByte();if (byteData[0] == 0x65) // e{ //byteData[1] = (byte)fstr.ReadByte(); if (byteData[1] == 0x6E) // n { //yteData[2] = (byte)fstr.ReadByte(); if (byteData[2] == 0x64) // d { //... } // e else { //if (byteData[6] == 0x65) //{ // dataIn.Add(byteData[0]); // dataIn.Add(byteData[1]); // dataIn.Add(byteData[2]); // dataIn.Add(byteData[3]); // dataIn.Add(byteData[4]); // dataIn.Add(byteData[5]); // fstr.Seek(-3, SeekOrigin.Current); //} //else { dataIn.Add(byteData[0]); //...
  68. 68. pos = pos + 9;if (byteData[0] == 0x65) // e{ if (byteData[1] == 0x6E) // n { if (byteData[2] == 0x64) // d { //... } // e else { dataIn.Add(byteData[0]); //...
  69. 69. Redundant CommentsComments related to structure
  70. 70. //// Upgrade section//if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" ){ Int32 retVal = ALLOK; //.... //.... //.... //.... //.... //.... //after 336 more lines of code...} // End of Upgrade section
  71. 71. manageUpdate(param);
  72. 72. Functions
  73. 73. FunctionsLong Methods
  74. 74. protected void build(DynamicView view, File baseTemplate, File outTemplate)throws ArchiverException, WrEntsException { //Create required data and column description List colNames = normalize(view.getDynamicColumnSet()); HashMap lines = new HashMap(); List leftNames = new ArrayList(); List rightNames = new ArrayList(); //Start reading input template and create output copy BufferedReader r = null; FileWriter w = null; if (outTemplate.exists()) { outTemplate.delete(); } try { r = new BufferedReader(new FileReader(baseTemplate)); w = new FileWriter(outTemplate); String record = null; boolean sortedBlock = false; while ((record = r.readLine()) != null) { if (sortedBlock) { if (record.toUpperCase().indexOf( "{END_SORTED_RECS}") >= 0) { sortedBlock = false; //Writes required records String line = null; //Static first line (if any)... for (int j = 0; j < leftNames.size(); j++) { line = (String)lines.get(leftNames.get(j)); if (line != null) { w.write(line + "n"); } }
  75. 75. //Sorted lines for (int j = 0; j < colNames.size(); j++) { line = (String)lines.get(colNames.get(j)); if (line != null) { w.write(line + "n"); } } w.write(record + "n"); //Static last line (if any)... for (int j = 0; j < rightNames.size(); j++) { line = (String)lines.get(rightNames.get(j)); if (line != null) { w.write(line + "n"); } } } else { int index = record.indexOf("{META_REC:"); if (index >= 0) { String key = record.substring(index + 10, record.indexOf(}, index)); if (key.indexOf(":") >= 0) { String values[] = key.split(":"); if (values[1].equals("L")) { leftNames.add(key); } else if (values[1].equals("R")) { rightNames.add(key); } } lines.put(key, new String(record)); } }}
  76. 76. else { if (record.toUpperCase().indexOf("{START_SORTED_RECS}") >= 0) { sortedBlock = true; lines.clear(); leftNames.clear(); rightNames.clear(); } w.write(record + "n"); } } } catch (Exception e) { WrCheck.logError("Error build template: " + e.getMessage()); throw new ArchiverException(e); } finally { try { if (w != null) w.close(); if (r != null) r.close(); } catch (Exception e) {} }}
  77. 77. FunctionsDeeply nested methods
  78. 78. //From TE-PDF exctractorif (str_byteData.Contains("s")){ posE_byteData = str_byteData.IndexOf("s"); pos = pos + (posE_byteData - 6); Array.Copy(fileContent, pos, byteData, 0, 6); pos = pos + 6; if (byteData[0] == 0x73) // s { if (byteData[1] == 0x74) // t { if (byteData[2] == 0x72) // r { if (byteData[3] == 0x65) // e { if (byteData[4] == 0x61) // a { if (byteData[5] == 0x6D) // m { found = true; break; } else { if (byteData[5] == 0x73) { pos = pos - 1; } } }
  79. 79. else { if (byteData[4] == 0x73) { pos = pos - 2; } else { pos = pos - 1; } } } else { if (byteData[3] == 0x73) { pos = pos - 3; } else { pos = pos - 2; } } } else { if (byteData[2] == 0x73) { pos = pos - 4; } else { pos = pos - 3; } }}
  80. 80. else { if (byteData[1] == 0x73) { pos = pos - 5; } else { pos = pos - 4; } } } else { pos = pos - 5; }}
  81. 81. FunctionsDifferent abstraction levelComposed methodDivide your program into methods thatperform one identifiable task
  82. 82. public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { /////si aggancia al file salvato nella mappa delloggetto application String fileId = request.getParameter("FILEID"); if (fileId == null) { throw new ServletException("Invalid FileId"); } String partNum = request.getParameter("PART"); int part = 1; if (partNum != null) { part = Integer.valueOf(partNum).intValue(); } boolean isLast = "Y".equals(request.getParameter("LAST")); boolean getName = "Y".equals(request.getParameter("GETNAME")); String fileName = MitoDownloadCache.INSTANCE.getFileName(fileId, part); if (fileName== null) { throw new ServletException("Invalid FileName"); } MitoConfig mitoCfg = new MitoConfig(); File file = new File(mitoCfg.getPrintClientDataPath()+"/"+fileName); if (!file.exists()) { throw new ServletException("File " + file.getAbsolutePath() + " not found"); } if (getName) { doDownloadFilename(request, response, file.getName()); } else { if (isLast) { MitoDownloadCache.INSTANCE.removeFileList(fileId); } doDownload(request, response, file); file.delete(); }
  83. 83. public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { File file = fileToDownLoad(request); if (downloadByName(request)) { doDownloadFilename(request, response, file.getName()); } else { removeFromCacheIfLast(request); doDownload(request, response, fileToDownLoad(request)); file.delete(); }}
  84. 84. private boolean downloadByName(HttpServletRequest request) { boolean getName = "Y".equals(request.getParameter("GETNAME")); return getName;}private void removeFromCacheIfLast(HttpServletRequest request) throws ServletException { boolean isLast = "Y".equals(request.getParameter("LAST")); if (isLast) { MitoDownloadCache.INSTANCE.removeFileList(fileId(request)); }}private File fileToDownLoad(HttpServletRequest request) throws ServletException { File file; String fileName = MitoDownloadCache.INSTANCE.getFileName( fileId(request), part(request)); if (fileName == null) { throw new ServletException("Invalid FileName"); } MitoConfig mitoCfg = new MitoConfig(); file = new File(mitoCfg.getPrintClientDataPath() + "/" + fileName); if (!file.exists()) { throw new ServletException("File " + file.getAbsolutePath() + " not found"); } return file;}
  85. 85. private int part(HttpServletRequest request) { String partNum = request.getParameter("PART"); int part = 1; if (partNum != null) { part = Integer.valueOf(partNum).intValue(); } return part;}private String fileId(HttpServletRequest request) throws ServletException { String fileId = request.getParameter("FILEID"); if (fileId == null) { throw new ServletException("Invalid FileId"); } return fileId;}
  86. 86. General
  87. 87. DuplicationDry“Once, and only once”Duplication is a missed opportunity forabstraction
  88. 88. Dead CodeCode that isn’t executedif statement that checksfor a condition that can’t happenPrivate method never calledswitch/case conditions thatnever occur Do the right thing: give it a decent burial
  89. 89. Vertical SeparationVariables and function should be defined closeto where they are usedLocal variablesshould be declared just above their first usage
  90. 90. public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { /////si aggancia al file salvato nella mappa delloggetto application String fileId = request.getParameter("FILEID"); if (fileId == null) { throw new ServletException("Invalid FileId"); } String partNum = request.getParameter("PART"); int part = 1; if (partNum != null) { part = Integer.valueOf(partNum).intValue(); } boolean isLast = "Y".equals(request.getParameter("LAST")); boolean getName = "Y".equals(request.getParameter("GETNAME")); String fileName = MitoDownloadCache.INSTANCE.getFileName(fileId, part); if (fileName== null) { throw new ServletException("Invalid FileName"); } MitoConfig mitoCfg = new MitoConfig(); File file = new File(mitoCfg.getPrintClientDataPath()+"/"+fileName); if (!file.exists()) { throw new ServletException("File " + file.getAbsolutePath() + " not found"); } if (getName) { doDownloadFilename(request, response, file.getName()); } else { if (isLast) { MitoDownloadCache.INSTANCE.removeFileList(fileId); } doDownload(request, response, file); file.delete(); }
  91. 91. public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { /////si aggancia al file salvato nella mappa delloggetto application String fileId = request.getParameter("FILEID"); if (fileId == null) { throw new ServletException("Invalid FileId"); } String partNum = request.getParameter("PART"); int part = 1; if (partNum != null) { part = Integer.valueOf(partNum).intValue(); } String fileName = MitoDownloadCache.INSTANCE.getFileName(fileId, part); if (fileName== null) { throw new ServletException("Invalid FileName"); } MitoConfig mitoCfg = new MitoConfig(); File file = new File(mitoCfg.getPrintClientDataPath()+"/"+fileName); if (!file.exists()) { throw new ServletException("File " + file.getAbsolutePath() + " not found"); } boolean getName = "Y".equals(request.getParameter("GETNAME")); if (getName) { doDownloadFilename(request, response, file.getName()); } else { boolean isLast = "Y".equals(request.getParameter("LAST")); if (isLast) { MitoDownloadCache.INSTANCE.removeFileList(fileId); } doDownload(request, response, file); file.delete(); }}
  92. 92. ClutterDefault constructor without implementationVariables not usedFunctions never calledUseless commentsKill’em all!
  93. 93. Selector ArgumentsAvoid boolean argumentsBreaks SRPSplit method in two methods
  94. 94. getLocalPrintService(jobId, data, false);public static PrintServiceInterface getLocalPrintService( String jobId, IRawData data, boolean isStandAlone) throws Exception{ //...}
  95. 95. public static PrintServiceInterface getEmbeddedLocalPrintService( String jobId, IRawData data) throws Exception{ //...}public static PrintServiceInterface getStandAloneLocalPrintService( String jobId, IRawData data) throws Exception{ //...}
  96. 96. Explanatory VarsAvoid ‘artistic’ programmingBreak the calculations up
  97. 97. Matcher match = headerPattern.matcher(line);if(match.find()) headers.put(match.group(1), match.group(2));
  98. 98. Matcher match = headerPattern.matcher(line);if(match.find()){ String key = match.group(1); String value = match.group(2); headers.put(key.toLowerCase(), value);}
  99. 99. Magic NumbersReplace Magic Numbers with Named Constants
  100. 100. m_tx1.setPageHeight(16837);m_tx1.setPageWidth(11906);m_tx1.setPageMarginB((int)(0.35 * 1440));m_tx1.setPageMarginL((int)(0.35 * 1440));m_tx1.setPageMarginR((int)(0.35 * 1440));m_tx1.setPageMarginT((int)(0.35 * 1440));m_tx2.setPageMarginB((int)(0.35 * 1440));m_tx2.setPageMarginL((int)(0.35 * 1440));m_tx2.setPageMarginR((int)(0.35 * 1440));m_tx2.setPageMarginT((int)(0.35 * 1440));
  101. 101. final static double PAGE_HEIGHT = 16837;final static double PAGE_WIDTH = 11906;final static int MARGIN = (int)(0.35 * 1440;)m_tx1.setPageHeight(PAGE_HEIGHT);m_tx1.setPageWidth(PAGE_WIDTH);m_tx1.setPageMarginB(MARGIN);m_tx1.setPageMarginL(MARGIN);m_tx1.setPageMarginR(MARGIN);m_tx1.setPageMarginT(MARGIN);m_tx2.setPageMarginB(MARGIN);m_tx2.setPageMarginL(MARGIN);m_tx2.setPageMarginR(MARGIN);m_tx2.setPageMarginT(MARGIN);
  102. 102. Negative ConditionalsNegatives is harder to understand than positivesAvoid negatives Conditionals
  103. 103. if (!buffer.shouldNotCompact()) { //...}if(!envWarrantsEnabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  104. 104. if (buffer.shouldCompact()) { //...}if(envWarrantsDisabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  105. 105. Names
  106. 106. Descriptive NamesChoice descriptive namesReevaluate the appropriateness of the names
  107. 107. public int x() { int q = 0; int z = 0; for (int kk = 0; kk < 10; kk++) { if (l[z] == 10) { q += 10 + (l[z + 1] + l[z + 2]); z += 1; } else if (l[z] + l[z + 1] == 10) { q += 10 + l[z + 2]; z += 2; } else { q += l[z] + l[z + 1]; z += 2; } } return q;}
  108. 108. public int score() { int score = 0; int frame = 0; for (int frameNumber = 0; frameNumber < 10; frameNumber++) { if (isStrike(frame)) { score += 10 + nextTwoBallsForStrike(frame); frame += 1; } else if (isSpare(frame)) { score += 10 + nextBallForSpare(frame); frame += 2; } else { score += twoBallsInFrame(frame); frame += 2; } } return score;}
  109. 109. Unambiguous NamesChoose names that make the workings of afunction or variable unambiguous
  110. 110. public boolean stateMachine(int smStatus) { //...}public boolean doAction() { //...}
  111. 111. public boolean moveStateMachineToStatus(int smStatus) { //...}public boolean doNextStepInProviderTechnique() { //...}
  112. 112. Avoid EncodingsNames should not be encoded with type orscope informationHungarian Notation as obsolete legacy
  113. 113. protected int m_FilesCount; // Numero files contenuti nel jobprotected int m_VolumesCount; // Numero dischi richiesti dal job (1 sola copia)protected long m_TotalSize; // Size totale in byte del jobprotected int m_VolumesDone; // Numero dischi completatiprotected String m_VolDoneList; // Lista dischi completati (durante esecuzione)protected ArrayList m_labelFields; // Nomi/valori campi da utilizzare in labelprivate long m_LastModTime; // Data/ora modifica file informativo del jobprotected boolean isOnDisk;
  114. 114. protected int filesCount; // Numero files contenuti nel jobprotected int volumesCount; // Numero dischi richiesti dal job (1 sola copia)protected long totalSize; // Size totale in byte del jobprotected int volumesDone; // Numero dischi completatiprotected String volDoneList; // Lista dischi completati (durante esecuzione)protected ArrayList labelFields; // Nomi/valori campi da utilizzare in labelprivate long lastModTime; // Data/ora modifica file informativo del jobprotected boolean isOnDisk;
  115. 115. Describe Side EffectsNames should describe everything that afunction, variable, or class is or does
  116. 116. public DiskWriterJobs getJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}
  117. 117. public DiskWriterJobs createOrReturnJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}
  118. 118. Tests
  119. 119. Insufficient TestsHow many tests? A test suite should test everything that could possibly breakUse a Coverage Tool: Java EMMA Cobertura Clover .NET PartCover C/C++ BullseyeCoverage gcov Beware of Coverage Results
  120. 120. Don’t Skip Trivial TestsThey are better than documentationEasy today, maybe hard tomorrow
  121. 121. Exhaustively Test Near BugBugs tend to congregate
  122. 122. Tests Should Be FastIf slow, launched less frequentelyIf launched less frequentely, big change between launches and difficult toknow where and when an error was introduced
  123. 123. Conclusions?
  124. 124. Readable Code
  125. 125. Test
  126. 126. Avoid Duplication
  127. 127. Readable Code Test Avoid Duplication
  128. 128. Q&A?
  129. 129. http://creativecommons.org/licenses/by-nc-sa/3.0/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×