better software: introduction to good code

133
Be#er So(ware An introduc2on to good code Giordano Scalzo, 06/05/2009

Post on 19-Oct-2014

2.656 views

Category:

Technology


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Better Software: introduction to good code

Be#er  So(wareAn  introduc2on  to  good  code

   Giordano  Scalzo,  06/05/2009

Page 2: Better Software: introduction to good code

• 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)

Page 3: Better Software: introduction to good code

Disclaimer

Page 4: Better Software: introduction to good code

I’m not here to preach

Page 5: Better Software: introduction to good code

Ward Cunningham40 years experience

Wiki inventor

Extreme Programming pioneer

Director of Eclipse

Page 6: Better Software: introduction to good code

Kent Beck

Extreme Programming inventor

JUnit inventor

Tdd inventor

Page 7: Better Software: introduction to good code

Robert C. Martin

Extreme Programming pioneer

Uncle Bob

40 years experience

Page 8: Better Software: introduction to good code
Page 9: Better Software: introduction to good code
Page 10: Better Software: introduction to good code
Page 11: Better Software: introduction to good code

What is Good Code?

Page 12: Better Software: introduction to good code

Bad Code

Can make scared!

Page 13: Better Software: introduction to good code

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; } } }

Page 14: Better Software: introduction to good code
Page 15: Better Software: introduction to good code

Theory of Broken Window

Page 16: Better Software: introduction to good code

Big Redesign in the Sky

Page 17: Better Software: introduction to good code

Netscape

rewrote Netscape 4.0 and released it after three years as Netscape 6.0

Page 18: Better Software: introduction to good code

Borland

rewrote dBase and Quattro Pro

Page 19: Better Software: introduction to good code

Microsoft

rewrote Word

Page 20: Better Software: introduction to good code

The Boy Scout Rule

Leave the campground cleaner than you found it

Page 21: Better Software: introduction to good code

OOP Principles

Page 22: Better Software: introduction to good code

Solid PrinciplesS.o.l.i.d. Principles

Page 23: Better Software: introduction to good code

Single Responsibility Principle

Only one reason to change

Robustness

Focus

Page 24: Better Software: introduction to good code

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) { //... }

// ...}

Page 25: Better Software: introduction to good code

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){ //... }}

Page 26: Better Software: introduction to good code

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); } //...}

Page 27: Better Software: introduction to good code

OpenClose Principle

open for extension

close for modification

abstraction

Page 28: Better Software: introduction to good code

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(...);}

Page 29: Better Software: introduction to good code

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 ""; }}

Page 30: Better Software: introduction to good code

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"; }}

Page 31: Better Software: introduction to good code

//...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(...);}//...

Page 32: Better Software: introduction to good code

Liskov Substitution Principle

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T

Page 33: Better Software: introduction to good code

Liskov Substitution Principle

Subtypes must be substitutable for their base types

Inheritance and polymorphism

Page 34: Better Software: introduction to good code

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; }}

Page 35: Better Software: introduction to good code

[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); }}

Page 36: Better Software: introduction to good code

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; }}

Page 37: Better Software: introduction to good code

Interface Segregation Principle

Don’t be force to implement unused methods

Avoid “Fat Interfaces”

High cohesion - better understandability, robustness

Low coupling - better maintainability, high resistance to changes

Page 38: Better Software: introduction to good code

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); } //.....}

Page 39: Better Software: introduction to good code

public abstract class Layer implements CartographyListener, DrawingInterface { /* Methods from CartographyListener interface */ // Metto qui un'implementazione 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 */

//.....}

Page 40: Better Software: introduction to good code

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(); }

//.....}

Page 41: Better Software: introduction to good code

public interface CartographyListener {}

public interface CellListener extends CartographyListener { public void cellsChanged(Locations cells);}

public interface PoiListener extends CartographyListener { public void poisChanged(Locations locations);}

Page 42: Better Software: introduction to good code

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); // ... } }

Page 43: Better Software: introduction to good code

Dependency Injection Principle

Hollywood Principle:

Inversion of Control

"don't call us, we will call you"

Dip <> Spring

Page 44: Better Software: introduction to good code

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);}

Page 45: Better Software: introduction to good code

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(){ //... }}

Page 46: Better Software: introduction to good code

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; } //...}

Page 47: Better Software: introduction to good code

The Others Principles

Page 48: Better Software: introduction to good code

Reuse Release Equivalency Principle

The granule of reuse is the granule of release

A package can be considered unit of distribution

A release should have a version number

Black-box, package that is to be used but not changed

Page 49: Better Software: introduction to good code

Common Closure Principle

Maintainability is more important than reusability

If code must change, all changes should be in the same package

Common changing classes, should be in the same package

Page 50: Better Software: introduction to good code

Common Reuse Principle

The classes in a package are reused together

If you reuse one of the classes in a package, you reuse them all

Page 51: Better Software: introduction to good code

Acyclic Dependencies Principle

Avoid cyclic dependencies

Nightmare to compile

Nightmare to deploy

Page 52: Better Software: introduction to good code

Least Astonishment Principle

The result of some operation should be obvious, consistent, predictable

Occam’s Razor: The simplest answer is usually the correct answer

Page 53: Better Software: introduction to good code

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 */ }

Page 54: Better Software: introduction to good code

Law of Demeter

Encapsulation

An object should avoid invoking methods of a member object returned by another method

“Don’t talk to stranger"

An object A can request a service (call a method) of an object instance B, but object A cannot “reach through” object B to access yet another object, C, to request its services

“Use only one dot"

Page 55: Better Software: introduction to good code

Inventory.SalesInfo.Items.Count = 2500;

Room.getPlan().canCustomizeWindow()Room.getPlan().canSelectStyle()Room.getPlan().hasCeilingFan()

Page 56: Better Software: introduction to good code

Inventory.SetSalesItemsCount(2500);

Room.canCustomizeWindow()Room.canSelectStyle()Room.hasCeilingFan()

Page 57: Better Software: introduction to good code

Smells and Heuristics

Page 58: Better Software: introduction to good code

Comments

Page 59: Better Software: introduction to good code

Obsolete CommentsOld comments that have lost their meaning

Page 60: Better Software: introduction to good code

//***********************************************************************************//! 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;}

Page 61: Better Software: introduction to good code

public void initLog(log4net.ILog inLog){ log = inLog; log.Debug("============ INIT Module " + getModuleName() + " =========");}

Page 62: Better Software: introduction to good code

Redundant CommentsVersion History

Page 63: Better Software: introduction to good code

/** * ** 03 Oct 2005 - AB - Added the isSameCIDinSameLiuID() function to avoid different CID in the same LIU (Ticket#2564) * 09 Sep 2005 - AB - fixed the retriveCallSideInfo() for the PhoneMatch method (Ticket#2381) * 06 Sep 2005 - AB - Fixed the SearchProviderDate() to properly work with the 'DATA' association technique * 01 Sep 2005 - AB - Added the dupval index exception handling in saveInHiddenJournal() function * 27 Jul 2005 - AB - changed the isInformationInDb() to avoid exiting with assocInfo.lemfList == null * 27 Jul 2005 - AB - removed the updateJournal() function because not needed * 26 Jul 2005 - AB - Now mergeJournal() saves a copy of the two lius ti be merged in the hidden_journal table * 26 Jul 2005 - AB - Added the saveInHiddenJournal() function to enhance the mergeJournal() function * 05 Jul 2005 - AB - Changed the retriveCallSideInfo queries to select the correct liu_id in every situation.… * 23 Mar 2005 - AB - Added the ORA-00001 error handling in the AddIRI2Journal * 9 Mar 2005 - AB - moved the queryExec body function to a generic queryExec function in the IRITools * 11 May 2004 - AB - Started **/

Page 64: Better Software: introduction to good code

svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java

Page 65: Better Software: introduction to good code

Redundant Comments

Repeating the variable name or condition in the comment

Page 66: Better Software: introduction to good code

//// list on a file all the Extractors extensions//if ( param == "-LISTEXTRACTORS" )//...

Page 67: Better Software: introduction to good code

Redundant Comments

Repeating the called method name in a comment after the call

Page 68: Better Software: introduction to good code

int abs = x.abs(); // Get the absolute value of xint x = point.getX(); // Get the value of x

Page 69: Better Software: introduction to good code

int abs = x.abs(); int x = point.getX();

Page 70: Better Software: introduction to good code

Redundant Comments

Commented Out Code

Page 71: Better Software: introduction to good code

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]); //...

Page 72: Better Software: introduction to good code

pos = pos + 9;if (byteData[0] == 0x65) // 'e'{ if (byteData[1] == 0x6E) // 'n' { if (byteData[2] == 0x64) // 'd' { //... } // 'e' else { dataIn.Add(byteData[0]); //...

Page 73: Better Software: introduction to good code

Redundant Comments

Comments related to structure

Page 74: Better Software: introduction to good code

//// Upgrade section//if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" ){ Int32 retVal = ALLOK; //.... //.... //.... //.... //.... //.... //after 336 more lines of code...} // End of Upgrade section

Page 75: Better Software: introduction to good code

manageUpdate(param);

Page 76: Better Software: introduction to good code

Functions

Page 77: Better Software: introduction to good code

Functions

Long Methods

Page 78: Better Software: introduction to good code

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"); } }

Page 79: Better Software: introduction to good code

//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)); } } }

Page 80: Better Software: introduction to good code

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) {} }}

Page 81: Better Software: introduction to good code

Functions

Deeply nested methods

Page 82: Better Software: introduction to good code

//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; } } }

Page 83: Better Software: introduction to good code

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; } } }

Page 84: Better Software: introduction to good code

else { if (byteData[1] == 0x73) { pos = pos - 5; } else { pos = pos - 4; } } } else { pos = pos - 5; }}

Page 85: Better Software: introduction to good code

Functions

Different abstraction level

Composed method

Divide your program into methods that perform one identifiable task

Page 86: Better Software: introduction to good code

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /////si aggancia al file salvato nella mappa dell'oggetto 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(); }}

Page 87: Better Software: introduction to good code

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(); }}

Page 88: Better Software: introduction to good code

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;}

Page 89: Better Software: introduction to good code

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;}

Page 90: Better Software: introduction to good code

General

Page 91: Better Software: introduction to good code

Duplication

Dry“Once, and only once”Duplication is a missed opportunity for abstraction

Page 92: Better Software: introduction to good code

Dead Code

Code that isn’t executed

if statement that checksfor a condition that can’t happen

Private method never called

switch/case conditions thatnever occur

Do the right thing: give it a decent burial

Page 93: Better Software: introduction to good code

Vertical Separation

Variables and function should be defined close to where they are used

Local variablesshould be declared just above their first usage

Page 94: Better Software: introduction to good code

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /////si aggancia al file salvato nella mappa dell'oggetto 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(); }}

Page 95: Better Software: introduction to good code

public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /////si aggancia al file salvato nella mappa dell'oggetto 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(); }}

Page 96: Better Software: introduction to good code

Clutter

Default constructor without implementation

Variables not used

Functions never called

Useless comments

Kill’em all!

Page 97: Better Software: introduction to good code

Selector Arguments

Avoid boolean arguments

Breaks SRPSplit method in two methods

Page 98: Better Software: introduction to good code

getLocalPrintService(jobId, data, false);

public static PrintServiceInterface getLocalPrintService( String jobId, IRawData data, boolean isStandAlone) throws Exception{ //...}

Page 99: Better Software: introduction to good code

public static PrintServiceInterface getEmbeddedLocalPrintService( String jobId, IRawData data) throws Exception{ //...}

public static PrintServiceInterface getStandAloneLocalPrintService( String jobId, IRawData data) throws Exception{ //...}

Page 100: Better Software: introduction to good code

Explanatory Vars

Avoid ‘artistic’ programming

Break the calculations up

Page 101: Better Software: introduction to good code

Matcher match = headerPattern.matcher(line);if(match.find()) headers.put(match.group(1), match.group(2));

Page 102: Better Software: introduction to good code

Matcher match = headerPattern.matcher(line);if(match.find()){ String key = match.group(1); String value = match.group(2); headers.put(key.toLowerCase(), value);}

Page 103: Better Software: introduction to good code

Magic Numbers

Replace Magic Numbers with Named Constants

Page 104: Better Software: introduction to good code

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));

Page 105: Better Software: introduction to good code

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);

Page 106: Better Software: introduction to good code

Negative Conditionals

Negatives is harder to understand than positives

Avoid negatives Conditionals

Page 107: Better Software: introduction to good code

if (!buffer.shouldNotCompact()) { //...}

if(!envWarrantsEnabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));

Page 108: Better Software: introduction to good code

if (buffer.shouldCompact()) { //...}

if(envWarrantsDisabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));

Page 109: Better Software: introduction to good code

Names

Page 110: Better Software: introduction to good code

Descriptive Names

Choice descriptive namesReevaluate the appropriateness of the names

Page 111: Better Software: introduction to good code

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;}

Page 112: Better Software: introduction to good code

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;}

Page 113: Better Software: introduction to good code

Unambiguous Names

Choose names that make the workings of a function or variable unambiguous

Page 114: Better Software: introduction to good code

public boolean stateMachine(int smStatus) { //...}

public boolean doAction() { //...}

Page 115: Better Software: introduction to good code

public boolean moveStateMachineToStatus(int smStatus) { //...}

public boolean doNextStepInProviderTechnique() { //...}

Page 116: Better Software: introduction to good code

Avoid Encodings

Names should not be encoded with type or scope information

Hungarian Notation as obsolete legacy

Page 117: Better Software: introduction to good code

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;

Page 118: Better Software: introduction to good code

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;

Page 119: Better Software: introduction to good code

Describe Side Effects

Names should describe everything that a function, variable, or class is or does

Page 120: Better Software: introduction to good code

public DiskWriterJobs getJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}

Page 121: Better Software: introduction to good code

public DiskWriterJobs createOrReturnJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs;}

Page 122: Better Software: introduction to good code

Tests

Page 123: Better Software: introduction to good code

Insufficient Tests

How many tests? A test suite should test everything that could possibly break

Use a Coverage Tool: Java

EMMACoberturaClover

.NET PartCoverC/C++ BullseyeCoverage gcov

Beware of Coverage Results

Page 124: Better Software: introduction to good code

Don’t Skip Trivial Tests

They are better than documentation

Easy today, maybe hard tomorrow

Page 125: Better Software: introduction to good code

Exhaustively Test Near Bug

Bugs tend to congregate

Page 126: Better Software: introduction to good code

Tests Should Be Fast

If slow, launched less frequentely

If launched less frequentely, big change between launches and difficult to know where and when an error was introduced

Page 127: Better Software: introduction to good code

Conclusions?

Page 128: Better Software: introduction to good code

Readable Code

Page 129: Better Software: introduction to good code

Test

Page 130: Better Software: introduction to good code

Avoid Duplication

Page 131: Better Software: introduction to good code

Readable Code

Test

Avoid Duplication

Page 132: Better Software: introduction to good code

Q&A?

Page 133: Better Software: introduction to good code

http://creativecommons.org/licenses/by-nc-sa/3.0/