SlideShare une entreprise Scribd logo
1  sur  133
Télécharger pour lire hors ligne
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 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)
Disclaimer
I’m not here to preach
Ward Cunningham
40 years experience
Wiki inventor
Extreme Programming pioneer
Director of Eclipse
Kent Beck

Extreme Programming inventor
JUnit inventor
Tdd inventor
Robert C. Martin

Uncle Bob
40 years experience
Extreme Programming pioneer
What is Good Code?
Bad Code
Can make scared!
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;
                                 }
                            }
                        }
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 Rule




Leave the campground cleaner than you found it
OOP Principles
S.o.l.i.d. Principles
 Solid Principles
Single Responsibility Principle


Only one reason to change

Robustness

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

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


open for extension

close for modification


abstraction
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(...);
}
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 "";
          }
}
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";
          }
}
//...
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(...);
}
//...
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
Liskov Substitution Principle


Subtypes must be substitutable for their base types



Inheritance and polymorphism
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;
    }
}
[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);
    }
}
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;
    }
}
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
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);
          }
          //.....
}
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 */

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

    //.....
}
public interface CartographyListener {
}

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

public interface PoiListener extends CartographyListener {
          public void poisChanged(Locations locations);
}
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);
             // ...
        }
    }

       // ...
}
Dependency Injection Principle

Inversion of Control

Hollywood Principle: "don't call us, we will call you"
Dip <> Spring
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);
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(){
        //...
    }
}
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;
    }
    //...
}
The Others Principles
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
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
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
Acyclic Dependencies Principle

Avoid cyclic dependencies
Nightmare to compile
Nightmare to deploy
Least Astonishment Principle

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

Occam’s Razor: The simplest answer is usually the correct answer
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 */
 }
Law of Demeter

Encapsulation
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
An object should avoid invoking methods of
a member object returned by another method
“Don’t talk to stranger"

“Use only one dot"
Inventory.SalesInfo.Items.Count = 2500;

Room.getPlan().canCustomizeWindow()
Room.getPlan().canSelectStyle()
Room.getPlan().hasCeilingFan()
Inventory.SetSalesItemsCount(2500);

Room.canCustomizeWindow()
Room.canSelectStyle()
Room.hasCeilingFan()
Smells and Heuristics
Comments
Obsolete Comments
Old comments that have lost their meaning
//***********************************************************************************
//! 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;
}
public void initLog(log4net.ILog inLog){
    log = inLog;
    log.Debug("============ INIT Module " + getModuleName() + " =========");
}
Redundant Comments
Version History
/**
  *
  *
* 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
  **/
svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java
Redundant Comments
Repeating the variable name or condition in the comment
//
// list on a file all the Extractors extensions
//
if ( param == "-LISTEXTRACTORS" )
//...
Redundant Comments
Repeating the called method name in a comment after the call
int abs = x.abs(); // Get the absolute value of x
int x = point.getX(); // Get the value of x
int abs = x.abs();
int x = point.getX();
Redundant Comments
Commented Out 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]);
                                  //...
pos = pos + 9;
if (byteData[0] == 0x65)      // 'e'
{
    if (byteData[1] == 0x6E)      // 'n'
    {
        if (byteData[2] == 0x64)      // 'd'
        {
            //...
                         }    // 'e'
                         else
                         {
                              dataIn.Add(byteData[0]);
                                  //...
Redundant Comments
Comments related to structure
//
//     Upgrade section
//
if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" )
{
     Int32 retVal = ALLOK;
     //....
     //....
     //....
     //....
     //....
     //....
     //after 336 more lines of code...
} //     End of Upgrade section
manageUpdate(param);
Functions
Functions
Long Methods
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");
                        }
                    }
//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));
        }
    }
}
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) {}
       }
}
Functions
Deeply nested methods
//From TE-PDF exctractor
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;
                              }
                         }
                    }
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;
           }
    }
}
else
           {
                  if (byteData[1] == 0x73)
                  {
                       pos = pos - 5;
                  }
                  else
                  {
                       pos = pos - 4;
                  }
           }
    }
    else
    {
           pos = pos - 5;
    }
}
Functions
Different abstraction level
Composed method
Divide your program into methods that
perform one identifiable task
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();
    }
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();
    }
}
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;
}
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;
}
General
Duplication
Dry
“Once, and only once”
Duplication is a missed opportunity for
abstraction
Dead Code
Code that isn’t executed
if statement that checks
for a condition that can’t happen
Private method never called
switch/case conditions that
never occur



 Do the right thing: give it a decent burial
Vertical Separation
Variables and function should be defined close
to where they are used


Local variables
should be declared just above their first usage
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();
    }
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();
    }
}
Clutter
Default constructor without implementation
Variables not used
Functions never called
Useless comments




Kill’em all!
Selector Arguments
Avoid boolean arguments
Breaks SRP
Split method in two methods
getLocalPrintService(jobId, data, false);


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

public static PrintServiceInterface getStandAloneLocalPrintService(
                                    String jobId, IRawData data) throws Exception{
    //...
}
Explanatory Vars
Avoid ‘artistic’ programming
Break 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.group(2);
    headers.put(key.toLowerCase(), value);
}
Magic Numbers
Replace Magic Numbers with Named Constants
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));
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);
Negative Conditionals
Negatives is harder to understand than positives
Avoid negatives Conditionals
if (!buffer.shouldNotCompact()) {
    //...
}

if(!envWarrantsEnabled)
    sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
if (buffer.shouldCompact()) {
    //...
}

if(envWarrantsDisabled)
    sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
Names
Descriptive Names
Choice descriptive names
Reevaluate 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 += 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;
}
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;
}
Unambiguous Names
Choose names that make the workings of a
function or variable unambiguous
public boolean stateMachine(int smStatus) {
    //...
}



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



public boolean doNextStepInProviderTechnique() {
    //...
}
Avoid Encodings
Names should not be encoded with type or
scope information
Hungarian Notation as obsolete legacy
protected   int         m_FilesCount;     //   Numero files contenuti nel job
protected   int         m_VolumesCount;   //   Numero dischi richiesti dal job (1 sola copia)
protected   long        m_TotalSize;      //   Size totale in byte del job
protected   int         m_VolumesDone;    //   Numero dischi completati
protected   String      m_VolDoneList;    //   Lista dischi completati (durante esecuzione)
protected   ArrayList   m_labelFields;    //   Nomi/valori campi da utilizzare in label
private     long        m_LastModTime;    //   Data/ora modifica file informativo del job
protected   boolean     isOnDisk;
protected   int         filesCount;     //   Numero files contenuti nel job
protected   int         volumesCount;   //   Numero dischi richiesti dal job (1 sola copia)
protected   long        totalSize;      //   Size totale in byte del job
protected   int         volumesDone;    //   Numero dischi completati
protected   String      volDoneList;    //   Lista dischi completati (durante esecuzione)
protected   ArrayList   labelFields;    //   Nomi/valori campi da utilizzare in label
private     long        lastModTime;    //   Data/ora modifica file informativo del job
protected   boolean     isOnDisk;
Describe Side Effects
Names should describe everything that a
function, 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);
    }
    return m_Jobs;
}
Tests
Insufficient Tests
How many tests? A test suite should test everything that could possibly break

Use a Coverage Tool:
     Java
         EMMA
         Cobertura
         Clover
     .NET
        PartCover
     C/C++
        BullseyeCoverage
        gcov

 Beware of Coverage Results
Don’t Skip Trivial Tests
They are better than documentation
Easy today, maybe hard tomorrow
Exhaustively Test Near Bug
Bugs tend to congregate
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
Conclusions?
Readable Code
Test
Avoid Duplication
Readable Code

        Test

    Avoid Duplication
Q&A?
http://creativecommons.org/licenses/by-nc-sa/3.0/

Contenu connexe

Tendances

ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation JavascriptRamesh Nair
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
An Intro To ES6
An Intro To ES6An Intro To ES6
An Intro To ES6FITC
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overviewhesher
 
Explaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to ComeExplaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to ComeCory Forsyth
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in RustIngvar Stepanyan
 
JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6Solution4Future
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)Pavlo Baron
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is hereSebastiano Armeli
 
Stamps - a better way to object composition
Stamps - a better way to object compositionStamps - a better way to object composition
Stamps - a better way to object compositionVasyl Boroviak
 
Rust concurrency tutorial 2015 12-02
Rust concurrency tutorial 2015 12-02Rust concurrency tutorial 2015 12-02
Rust concurrency tutorial 2015 12-02nikomatsakis
 
Rust Mozlando Tutorial
Rust Mozlando TutorialRust Mozlando Tutorial
Rust Mozlando Tutorialnikomatsakis
 
Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And BeyondMike Fogus
 

Tendances (20)

EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
Academy PRO: ES2015
Academy PRO: ES2015Academy PRO: ES2015
Academy PRO: ES2015
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
An Intro To ES6
An Intro To ES6An Intro To ES6
An Intro To ES6
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overview
 
Explaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to ComeExplaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to Come
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in Rust
 
JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6JavaScript - new features in ECMAScript 6
JavaScript - new features in ECMAScript 6
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
JavaScript ES6
JavaScript ES6JavaScript ES6
JavaScript ES6
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is here
 
Stamps - a better way to object composition
Stamps - a better way to object compositionStamps - a better way to object composition
Stamps - a better way to object composition
 
Rust ⇋ JavaScript
Rust ⇋ JavaScriptRust ⇋ JavaScript
Rust ⇋ JavaScript
 
Rust concurrency tutorial 2015 12-02
Rust concurrency tutorial 2015 12-02Rust concurrency tutorial 2015 12-02
Rust concurrency tutorial 2015 12-02
 
Rust Mozlando Tutorial
Rust Mozlando TutorialRust Mozlando Tutorial
Rust Mozlando Tutorial
 
Rust言語紹介
Rust言語紹介Rust言語紹介
Rust言語紹介
 
Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And Beyond
 

Similaire à Better Software: introduction to good code

[PL] O klasycznej, programistycznej elegancji
[PL] O klasycznej, programistycznej elegancji[PL] O klasycznej, programistycznej elegancji
[PL] O klasycznej, programistycznej elegancjiJakub Marchwicki
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages VictorSzoltysek
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeDaniel Wellman
 
05 pig user defined functions (udfs)
05 pig user defined functions (udfs)05 pig user defined functions (udfs)
05 pig user defined functions (udfs)Subhas Kumar Ghosh
 
C# 6.0 - April 2014 preview
C# 6.0 - April 2014 previewC# 6.0 - April 2014 preview
C# 6.0 - April 2014 previewPaulo Morgado
 
Ast transformations
Ast transformationsAst transformations
Ast transformationsHamletDRC
 
Paradigmas de linguagens de programacao - aula#9
Paradigmas de linguagens de programacao - aula#9Paradigmas de linguagens de programacao - aula#9
Paradigmas de linguagens de programacao - aula#9Ismar Silveira
 
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo....NET Conf UY
 
[2019-07] GraphQL in depth (serverside)
[2019-07] GraphQL in depth (serverside)[2019-07] GraphQL in depth (serverside)
[2019-07] GraphQL in depth (serverside)croquiscom
 
Best of build 2021 - C# 10 & .NET 6
Best of build 2021 -  C# 10 & .NET 6Best of build 2021 -  C# 10 & .NET 6
Best of build 2021 - C# 10 & .NET 6Moaid Hathot
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript IntroductionDmitry Sheiko
 
TDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETTDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETtdc-globalcode
 
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018Codemotion
 
3. Объекты, классы и пакеты в Java
3. Объекты, классы и пакеты в Java3. Объекты, классы и пакеты в Java
3. Объекты, классы и пакеты в JavaDEVTYPE
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
Слава Бобик «NancyFx для самых маленьких»
Слава Бобик «NancyFx для самых маленьких»Слава Бобик «NancyFx для самых маленьких»
Слава Бобик «NancyFx для самых маленьких»SpbDotNet Community
 

Similaire à Better Software: introduction to good code (20)

[PL] O klasycznej, programistycznej elegancji
[PL] O klasycznej, programistycznej elegancji[PL] O klasycznej, programistycznej elegancji
[PL] O klasycznej, programistycznej elegancji
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy Code
 
05 pig user defined functions (udfs)
05 pig user defined functions (udfs)05 pig user defined functions (udfs)
05 pig user defined functions (udfs)
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
C# 6.0 - April 2014 preview
C# 6.0 - April 2014 previewC# 6.0 - April 2014 preview
C# 6.0 - April 2014 preview
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Paradigmas de linguagens de programacao - aula#9
Paradigmas de linguagens de programacao - aula#9Paradigmas de linguagens de programacao - aula#9
Paradigmas de linguagens de programacao - aula#9
 
E:\Plp 2009 2\Plp 9
E:\Plp 2009 2\Plp 9E:\Plp 2009 2\Plp 9
E:\Plp 2009 2\Plp 9
 
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...
Code Smells y Refactoring o haciendo que nuestro codigo huela (y se vea) mejo...
 
[2019-07] GraphQL in depth (serverside)
[2019-07] GraphQL in depth (serverside)[2019-07] GraphQL in depth (serverside)
[2019-07] GraphQL in depth (serverside)
 
Best of build 2021 - C# 10 & .NET 6
Best of build 2021 -  C# 10 & .NET 6Best of build 2021 -  C# 10 & .NET 6
Best of build 2021 - C# 10 & .NET 6
 
Java Class Design
Java Class DesignJava Class Design
Java Class Design
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
 
TDC2016SP - Trilha .NET
TDC2016SP - Trilha .NETTDC2016SP - Trilha .NET
TDC2016SP - Trilha .NET
 
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
Kotlin for Android Developers - Victor Kropp - Codemotion Rome 2018
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
3. Объекты, классы и пакеты в Java
3. Объекты, классы и пакеты в Java3. Объекты, классы и пакеты в Java
3. Объекты, классы и пакеты в Java
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
Слава Бобик «NancyFx для самых маленьких»
Слава Бобик «NancyFx для самых маленьких»Слава Бобик «NancyFx для самых маленьких»
Слава Бобик «NancyFx для самых маленьких»
 

Plus de Giordano Scalzo

The Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentGiordano Scalzo
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to SwiftGiordano Scalzo
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperGiordano Scalzo
 
Better Software Developers
Better Software DevelopersBetter Software Developers
Better Software DevelopersGiordano Scalzo
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone DevelopmentGiordano Scalzo
 
XpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayGiordano Scalzo
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteGiordano Scalzo
 
10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual Resume10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual ResumeGiordano Scalzo
 

Plus de Giordano Scalzo (12)

The Joy Of Server Side Swift Development
The Joy Of Server Side Swift DevelopmentThe Joy Of Server Side Swift Development
The Joy Of Server Side Swift Development
 
A swift introduction to Swift
A swift introduction to SwiftA swift introduction to Swift
A swift introduction to Swift
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
 
Code kata
Code kataCode kata
Code kata
 
JavaScript Patterns
JavaScript PatternsJavaScript Patterns
JavaScript Patterns
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
 
Better Software Developers
Better Software DevelopersBetter Software Developers
Better Software Developers
 
Agile Iphone Development
Agile Iphone DevelopmentAgile Iphone Development
Agile Iphone Development
 
XpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp wayXpUg Coding Dojo: KataYahtzee in Ocp way
XpUg Coding Dojo: KataYahtzee in Ocp way
 
Bdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infiniteBdd: Tdd and beyond the infinite
Bdd: Tdd and beyond the infinite
 
10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual Resume10 minutes of me: Giordano Scalzo's Visual Resume
10 minutes of me: Giordano Scalzo's Visual Resume
 
Scrum in an hour
Scrum in an hourScrum in an hour
Scrum in an hour
 

Dernier

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 

Dernier (20)

Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 

Better Software: introduction to good code

  • 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)
  • 4. I’m not here to preach
  • 5. Ward Cunningham 40 years experience Wiki inventor Extreme Programming pioneer Director of Eclipse
  • 6. Kent Beck Extreme Programming inventor JUnit inventor Tdd inventor
  • 7. Robert C. Martin Uncle Bob 40 years experience Extreme Programming pioneer
  • 8.
  • 9.
  • 10.
  • 11. What is Good Code?
  • 12. Bad Code Can make scared!
  • 13. 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; } } }
  • 14.
  • 16. Big Redesign in the Sky
  • 17. Netscape rewrote Netscape 4.0 and released it after three years as Netscape 6.0
  • 18. Borland rewrote dBase and Quattro Pro
  • 19. Microsoft rewrote Word
  • 20. The Boy Scout Rule Leave the campground cleaner than you found it
  • 23. Single Responsibility Principle Only one reason to change Robustness Focus
  • 24. 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) { //... } // ... }
  • 25. 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){ //... } }
  • 26. 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); } //... }
  • 27. OpenClose Principle open for extension close for modification abstraction
  • 28. 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(...); }
  • 29. 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 ""; } }
  • 30. 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"; } }
  • 31. //... 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(...); } //...
  • 32. 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
  • 33. Liskov Substitution Principle Subtypes must be substitutable for their base types Inheritance and polymorphism
  • 34. 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; } }
  • 35. [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); } }
  • 36. 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; } }
  • 37. 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
  • 38. 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); } //..... }
  • 39. 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 */ //..... }
  • 40. 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(); } //..... }
  • 41. public interface CartographyListener { } public interface CellListener extends CartographyListener { public void cellsChanged(Locations cells); } public interface PoiListener extends CartographyListener { public void poisChanged(Locations locations); }
  • 42. 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); // ... } } // ... }
  • 43. Dependency Injection Principle Inversion of Control Hollywood Principle: "don't call us, we will call you" Dip <> Spring
  • 44. 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);
  • 45. 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(){ //... } }
  • 46. 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; } //... }
  • 48. 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
  • 49. 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
  • 50. 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
  • 51. Acyclic Dependencies Principle Avoid cyclic dependencies Nightmare to compile Nightmare to deploy
  • 52. Least Astonishment Principle The result of some operation should be obvious, consistent, predictable Occam’s Razor: The simplest answer is usually the correct answer
  • 53. 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 */ }
  • 54. Law of Demeter Encapsulation 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 An object should avoid invoking methods of a member object returned by another method “Don’t talk to stranger" “Use only one dot"
  • 59. Obsolete Comments Old comments that have lost their meaning
  • 60. //*********************************************************************************** //! 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; }
  • 61. public void initLog(log4net.ILog inLog){ log = inLog; log.Debug("============ INIT Module " + getModuleName() + " ========="); }
  • 63. /** * * * 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 **/
  • 64. svn ci -m ‘Added the isSameCIDinSameLiuID() (Ticket#2564)’ AssociationFunction.java
  • 65. Redundant Comments Repeating the variable name or condition in the comment
  • 66. // // list on a file all the Extractors extensions // if ( param == "-LISTEXTRACTORS" ) //...
  • 67. Redundant Comments Repeating the called method name in a comment after the call
  • 68. int abs = x.abs(); // Get the absolute value of x int x = point.getX(); // Get the value of x
  • 69. int abs = x.abs(); int x = point.getX();
  • 71. 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]); //...
  • 72. pos = pos + 9; if (byteData[0] == 0x65) // 'e' { if (byteData[1] == 0x6E) // 'n' { if (byteData[2] == 0x64) // 'd' { //... } // 'e' else { dataIn.Add(byteData[0]); //...
  • 74. // // Upgrade section // if ( param =="-GENERATEUPDATE" || param =="-DOWNLOADUPDATE" || param =="-CHECKUPDATE" ) { Int32 retVal = ALLOK; //.... //.... //.... //.... //.... //.... //after 336 more lines of code... } // End of Upgrade section
  • 78. 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"); } }
  • 79. //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)); } } }
  • 80. 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) {} } }
  • 82. //From TE-PDF exctractor 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; } } }
  • 83. 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; } } }
  • 84. else { if (byteData[1] == 0x73) { pos = pos - 5; } else { pos = pos - 4; } } } else { pos = pos - 5; } }
  • 85. Functions Different abstraction level Composed method Divide your program into methods that perform one identifiable task
  • 86. 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(); }
  • 87. 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(); } }
  • 88. 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; }
  • 89. 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; }
  • 91. Duplication Dry “Once, and only once” Duplication is a missed opportunity for abstraction
  • 92. Dead Code Code that isn’t executed if statement that checks for a condition that can’t happen Private method never called switch/case conditions that never occur Do the right thing: give it a decent burial
  • 93. Vertical Separation Variables and function should be defined close to where they are used Local variables should be declared just above their first usage
  • 94. 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(); }
  • 95. 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(); } }
  • 96. Clutter Default constructor without implementation Variables not used Functions never called Useless comments Kill’em all!
  • 97. Selector Arguments Avoid boolean arguments Breaks SRP Split method in two methods
  • 98. getLocalPrintService(jobId, data, false); public static PrintServiceInterface getLocalPrintService( String jobId, IRawData data, boolean isStandAlone) throws Exception{ //... }
  • 99. public static PrintServiceInterface getEmbeddedLocalPrintService( String jobId, IRawData data) throws Exception{ //... } public static PrintServiceInterface getStandAloneLocalPrintService( String jobId, IRawData data) throws Exception{ //... }
  • 100. Explanatory Vars Avoid ‘artistic’ programming Break the calculations up
  • 101. Matcher match = headerPattern.matcher(line); if(match.find()) headers.put(match.group(1), match.group(2));
  • 102. Matcher match = headerPattern.matcher(line); if(match.find()){ String key = match.group(1); String value = match.group(2); headers.put(key.toLowerCase(), value); }
  • 103. Magic Numbers Replace Magic Numbers with Named Constants
  • 104. 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));
  • 105. 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);
  • 106. Negative Conditionals Negatives is harder to understand than positives Avoid negatives Conditionals
  • 107. if (!buffer.shouldNotCompact()) { //... } if(!envWarrantsEnabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  • 108. if (buffer.shouldCompact()) { //... } if(envWarrantsDisabled) sqlSelect.addWhere(new SqlNot(new SqlEq("LI_TYPE", "ENV")));
  • 109. Names
  • 110. Descriptive Names Choice descriptive names Reevaluate the appropriateness of the names
  • 111. 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; }
  • 112. 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; }
  • 113. Unambiguous Names Choose names that make the workings of a function or variable unambiguous
  • 114. public boolean stateMachine(int smStatus) { //... } public boolean doAction() { //... }
  • 115. public boolean moveStateMachineToStatus(int smStatus) { //... } public boolean doNextStepInProviderTechnique() { //... }
  • 116. Avoid Encodings Names should not be encoded with type or scope information Hungarian Notation as obsolete legacy
  • 117. protected int m_FilesCount; // Numero files contenuti nel job protected int m_VolumesCount; // Numero dischi richiesti dal job (1 sola copia) protected long m_TotalSize; // Size totale in byte del job protected int m_VolumesDone; // Numero dischi completati protected String m_VolDoneList; // Lista dischi completati (durante esecuzione) protected ArrayList m_labelFields; // Nomi/valori campi da utilizzare in label private long m_LastModTime; // Data/ora modifica file informativo del job protected boolean isOnDisk;
  • 118. protected int filesCount; // Numero files contenuti nel job protected int volumesCount; // Numero dischi richiesti dal job (1 sola copia) protected long totalSize; // Size totale in byte del job protected int volumesDone; // Numero dischi completati protected String volDoneList; // Lista dischi completati (durante esecuzione) protected ArrayList labelFields; // Nomi/valori campi da utilizzare in label private long lastModTime; // Data/ora modifica file informativo del job protected boolean isOnDisk;
  • 119. Describe Side Effects Names should describe everything that a function, variable, or class is or does
  • 120. public DiskWriterJobs getJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs; }
  • 121. public DiskWriterJobs createOrReturnJobs() { if (m_Jobs == null) { m_Jobs = new DiskWriterJobs(this); } return m_Jobs; }
  • 122. Tests
  • 123. Insufficient Tests How many tests? A test suite should test everything that could possibly break Use a Coverage Tool: Java EMMA Cobertura Clover .NET PartCover C/C++ BullseyeCoverage gcov Beware of Coverage Results
  • 124. Don’t Skip Trivial Tests They are better than documentation Easy today, maybe hard tomorrow
  • 125. Exhaustively Test Near Bug Bugs tend to congregate
  • 126. 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
  • 129. Test
  • 131. Readable Code Test Avoid Duplication
  • 132. Q&A?