Better Software: introduction to good code

  • 2,510 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
2,510
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
171
Comments
0
Likes
21

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Be#er  So(ware An  introduc2on  to  good  code    Giordano  Scalzo,  06/05/2009
  • 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. Disclaimer
  • 4. I’m not here to preach
  • 5. Ward Cunningham40 years experienceWiki inventorExtreme Programming pioneerDirector of Eclipse
  • 6. Kent BeckExtreme Programming inventorJUnit inventorTdd inventor
  • 7. Robert C. MartinUncle Bob40 years experienceExtreme Programming pioneer
  • 8. What is Good Code?
  • 9. Bad CodeCan make scared!
  • 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. Theory of Broken Window
  • 12. Big Redesign in the Sky
  • 13. Netscape rewrote Netscape 4.0 and released it after three years as Netscape 6.0
  • 14. Borland rewrote dBase and Quattro Pro
  • 15. Microsoft rewrote Word
  • 16. The Boy Scout RuleLeave the campground cleaner than you found it
  • 17. OOP Principles
  • 18. S.o.l.i.d. Principles Solid Principles
  • 19. Single Responsibility PrincipleOnly one reason to changeRobustnessFocus
  • 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. 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. 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. OpenClose Principleopen for extensionclose for modificationabstraction
  • 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. 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. 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. //...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. 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. Liskov Substitution PrincipleSubtypes must be substitutable for their base typesInheritance and polymorphism
  • 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. [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. 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. 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. 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. 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. 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. public interface CartographyListener {}public interface CellListener extends CartographyListener { public void cellsChanged(Locations cells);}public interface PoiListener extends CartographyListener { public void poisChanged(Locations locations);}
  • 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. Dependency Injection PrincipleInversion of ControlHollywood Principle: "dont call us, we will call you"Dip <> Spring
  • 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. 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. 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. The Others Principles
  • 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. 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. Common Reuse PrincipleThe classes in a package are reused togetherIf you reuse one of the classes in a package, you reuse them all
  • 47. Acyclic Dependencies PrincipleAvoid cyclic dependenciesNightmare to compileNightmare to deploy
  • 48. Least Astonishment PrincipleThe result of some operation should be obvious, consistent, predictableOccam’s Razor: The simplest answer is usually the correct answer
  • 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. 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. Inventory.SalesInfo.Items.Count = 2500;Room.getPlan().canCustomizeWindow()Room.getPlan().canSelectStyle()Room.getPlan().hasCeilingFan()
  • 52. Inventory.SetSalesItemsCount(2500);Room.canCustomizeWindow()Room.canSelectStyle()Room.hasCeilingFan()
  • 53. Smells and Heuristics
  • 54. Comments
  • 55. Obsolete CommentsOld comments that have lost their meaning
  • 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. public void initLog(log4net.ILog inLog){ log = inLog; log.Debug("============ INIT Module " + getModuleName() + " =========");}
  • 58. Redundant CommentsVersion History
  • 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. svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java
  • 61. Redundant CommentsRepeating the variable name or condition in the comment
  • 62. //// list on a file all the Extractors extensions//if ( param == "-LISTEXTRACTORS" )//...
  • 63. Redundant CommentsRepeating the called method name in a comment after the call
  • 64. int abs = x.abs(); // Get the absolute value of xint x = point.getX(); // Get the value of x
  • 65. int abs = x.abs();int x = point.getX();
  • 66. Redundant CommentsCommented Out Code
  • 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. 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. Redundant CommentsComments related to structure
  • 70. //// Upgrade section//if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" ){ Int32 retVal = ALLOK; //.... //.... //.... //.... //.... //.... //after 336 more lines of code...} // End of Upgrade section
  • 71. manageUpdate(param);
  • 72. Functions
  • 73. FunctionsLong Methods
  • 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. //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. 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. FunctionsDeeply nested methods
  • 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. 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. else { if (byteData[1] == 0x73) { pos = pos - 5; } else { pos = pos - 4; } } } else { pos = pos - 5; }}
  • 81. FunctionsDifferent abstraction levelComposed methodDivide your program into methods thatperform one identifiable task
  • 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. 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. 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. 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. General
  • 87. DuplicationDry“Once, and only once”Duplication is a missed opportunity forabstraction
  • 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. Vertical SeparationVariables and function should be defined closeto where they are usedLocal variablesshould be declared just above their first usage
  • 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. 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. ClutterDefault constructor without implementationVariables not usedFunctions never calledUseless commentsKill’em all!
  • 93. Selector ArgumentsAvoid boolean argumentsBreaks SRPSplit method in two methods
  • 94. getLocalPrintService(jobId, data, false);public static PrintServiceInterface getLocalPrintService( String jobId, IRawData data, boolean isStandAlone) throws Exception{ //...}
  • 95. public static PrintServiceInterface getEmbeddedLocalPrintService( String jobId, IRawData data) throws Exception{ //...}public static PrintServiceInterface getStandAloneLocalPrintService( String jobId, IRawData data) throws Exception{ //...}
  • 96. Explanatory VarsAvoid ‘artistic’ programmingBreak the calculations up
  • 97. Matcher match = headerPattern.matcher(line);if(match.find()) headers.put(match.group(1), match.group(2));
  • 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. Magic NumbersReplace Magic Numbers with Named Constants
  • 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. 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. Negative ConditionalsNegatives is harder to understand than positivesAvoid negatives Conditionals
  • 103. if (!buffer.shouldNotCompact()) { //...}if(!envWarrantsEnabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  • 104. if (buffer.shouldCompact()) { //...}if(envWarrantsDisabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  • 105. Names
  • 106. Descriptive NamesChoice descriptive namesReevaluate the appropriateness of the names
  • 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. 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. Unambiguous NamesChoose names that make the workings of afunction or variable unambiguous
  • 110. public boolean stateMachine(int smStatus) { //...}public boolean doAction() { //...}
  • 111. public boolean moveStateMachineToStatus(int smStatus) { //...}public boolean doNextStepInProviderTechnique() { //...}
  • 112. Avoid EncodingsNames should not be encoded with type orscope informationHungarian Notation as obsolete legacy
  • 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. 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. Describe Side EffectsNames should describe everything that afunction, variable, or class is or does
  • 116. public DiskWriterJobs getJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}
  • 117. public DiskWriterJobs createOrReturnJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}
  • 118. Tests
  • 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. Don’t Skip Trivial TestsThey are better than documentationEasy today, maybe hard tomorrow
  • 121. Exhaustively Test Near BugBugs tend to congregate
  • 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. Conclusions?
  • 124. Readable Code
  • 125. Test
  • 126. Avoid Duplication
  • 127. Readable Code Test Avoid Duplication
  • 128. Q&A?
  • 129. http://creativecommons.org/licenses/by-nc-sa/3.0/