Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Clean Code Development

4,189 views

Published on

Good and Bad Code
The Broken Window Theory
The Grand Redesign in the Sky
The Sushi Chef Rule
The Hotel Room Rule
The Boy Scout Rule

OOP Patterns and Principles
SOLID Principles

How to measure clean code?
Tools

Published in: Technology
  • Nice presentation. Another habit that does wonders is choosing good names. However, this is obviously not always easy: http://martinfowler.com/bliki/TwoHardThings.html
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Clean Code Development

  1. 1. Clean Code Development Peter Gfader #netug Delivering Awesome Web Applications
  2. 2. C# and .NET (Java not anymore) Testing Automated tests Agile, Scrum Scrum Developer Trainer Technology aficionado Silverlight ASP.NET Windows Forms LINQ, ... Peter Gfader http://blog.gfader.com/ twitter.com/peitor #netug
  3. 3. • Why code matters • Good and Bad Code • The Broken Window Theory • The Grand Redesign in the Sky • The Boy Scout Rule • OOP Patterns and Principles • SOLID Principles • How to measure clean code? •Tools Agenda
  4. 4. 1903 Wright brothers flew 59 seconds Why code matters? http://www.reddit.com/r/AskReddit/comments/dlrjs/whats_the_most_mindblowing_fact_you_heardread_in/
  5. 5. 1969 We landed on the moon Why code matters?
  6. 6. Today We fly around the world in 32 hours Tourists in space Why code matters?
  7. 7. 1903 - 0 computers Why code matters?
  8. 8. 40 years later - a handful of computers Why code matters?
  9. 9. Today > 500 billion programmable devices Why code matters?
  10. 10. Today > 500 billion programmable devices (more than humans on earth) Why code matters?
  11. 11. 2100 Programmable devices everywhere Under skin, in brain, in blood… like dust… Why code matters?
  12. 12. Who programs those? Why code matters?
  13. 13. Why code matters? What tools do we use?
  14. 14. Why code matters? Can we trust our code? Important?
  15. 15. What is good code?
  16. 16. It's gotta ship? It's gotta pass the tester? It's gotta implement requirements? It's gotta be reasonably performant? "Wartung"? (aka Maintainability) What is good code?
  17. 17. What is bad code?
  18. 18. What is bad code?
  19. 19. What is bad code?
  20. 20. 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; } } } What is bad code?
  21. 21. 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; } What is bad code?
  22. 22. What is bad code?
  23. 23. • Hard to understand at first sight • Unmaintained • Messy • No one cares What is bad code?
  24. 24. • Hard to understand at first sight • Unmaintained • Messy • No one cares "Wartung"? (aka Maintainabilty) What is bad code?
  25. 25. Bad Code Is SCARY!!!
  26. 26. Why are we writing bad code?
  27. 27. Broken Window Theory
  28. 28. Broken Window Theory
  29. 29. The rewrite
  30. 30. Netscape rewrote Netscape 4.0 and released it after three years as Netscape 6.0
  31. 31. Borland rewrote dBase and Quattro Pro
  32. 32. Microsoft Rewrote Vista ~60%
  33. 33. What can we do?
  34. 34. Sushi chef rule Clean up as you do
  35. 35. Hotel room rule Let someone clean up every day
  36. 36. The Boy Scout Rule Leave the campground cleaner than you found it
  37. 37. How can we improve?
  38. 38. "Everything I have to change, in order to make the product owner happy!" • Config files .config, .svc • XAML .xaml, .CSS, .. • Code .cs, .vb, .js, .. • Deployment scripts .ps • Batch files .bat What is code?
  39. 39. OOP Principles
  40. 40. Solid PrinciplesS.o.l.i.d. Principles
  41. 41. Single Responsibility Principle Only one reason to change Robustness Focus Every entity should have a single responsibility
  42. 42. public class PrintServer { public string CreateJob(PrintJob data) { //... } public int GetStatus(string jobId) { //... } public void Print(string jobId, int startPage, int endPage) { //... } public List<Printer> GetPrinterList() { //... } public bool AddPrinter(Printer printer) { //... } public event EventHandler<JobEvent> PrintPreviewPageComputed; public event EventHandler PrintPreviewReady; // ... }
  43. 43. public class PrintServer { public string CreateJob(PrintJob data) { //... } public int GetStatus(string jobId) { //... } public void Print(string jobId, int startPage, int endPage) { //... } } public class PrinterList { public List<Printer> GetPrinterList() { //... } public bool AddPrinter(Printer printer) { //... } }
  44. 44. OpenClose Principle Open for extension Close for modification Every entity should be open for extension, but closed for modification
  45. 45. public void SaveToolbarStateSwitch() { long version = Core.GetVisualStudioVersion(); Configuration.VSToolbarHeight = AddinCommandBar.Height.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); switch (version) { case 2003: case 2004: case 2005: Configuration.VSToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); break; case 2008: Configuration.VSToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); break; case 2010: Configuration.VSToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VSToolbarLeft = AddinCommandBar.Left.ToString(); Configuration.VSToolbarTop = AddinCommandBar.Top.ToString(); Configuration.VSToolbarWidth = AddinCommandBar.Width.ToString(); break; default: Configuration.VS2010ToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VS2010ToolbarPosition = AddinCommandBar.Position.ToString(); break; }
  46. 46. public class ToolbarManager { public void SaveToolbarState() { var version = Core.GetVisualStudioVersion(); Configuration.VSToolbarHeight = AddinCommandBar.Height.ToString(); if (version <= 2003) { Configuration.VS2010ToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VS2010ToolbarPosition = AddinCommandBar.Position.ToString(); } else if (version >= 2005 && version <= 2008) { Configuration.VSToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); } else if (version == 2010) { Configuration.VSToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VSToolbarLeft = AddinCommandBar.Left.ToString(); Configuration.VSToolbarTop = AddinCommandBar.Top.ToString(); Configuration.VSToolbarWidth = AddinCommandBar.Width.ToString(); } else { Configuration.VSToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); } }
  47. 47. private void SaveForVs2003() { Configuration.VSToolbarVisible = "False"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); } private void SaveForVs2005() { Configuration.VSToolbarVisible = "True"; Configuration.VSToolbarPosition = ((int)AddinCommandBar.Position).ToString(); Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); Configuration.VS2010ToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; } private void SaveForVs2008() { Configuration.VSToolbarPosition = "False"; Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarVisible = "False"; Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); } private void SaveForVs2010() { Configuration.VSToolbarVisible = AddinCommandBar.Visible ? "True" : "False"; Configuration.VSToolbarPosition = "False"; Configuration.VS2010ToolbarRowIndex = AddinCommandBar.RowIndex.ToString(); Configuration.VS2010ToolbarWidth = AddinCommandBar.Width.ToString(); Configuration.VS2010ToolbarHeight = AddinCommandBar.Height.ToString(); }
  48. 48. public class ToolbarManager { private readonly Dictionary<long, Action> _versionAction; public ToolbarManager() { _versionAction = new Dictionary<long, Action>(); _versionAction.Add(2003, SaveForVs2003); _versionAction.Add(2005, SaveForVs2005); _versionAction.Add(2008, SaveForVs2008); _versionAction.Add(2010, SaveForVs2010); } public void SaveToolbarStateBetter() { var version = Core.GetVisualStudioVersion(); if (_versionAction.ContainsKey(version)) { _versionAction[version].Invoke(); } }
  49. 49. public class ToolbarManager { private readonly Dictionary<long, Action> _versionAction; public ToolbarManager() { _versionAction = new Dictionary<long, Action>(); _versionAction.Add(2003, SaveForVs2003); _versionAction.Add(2005, SaveForVs2005); _versionAction.Add(2008, SaveForVs2008); _versionAction.Add(2010, SaveForVs2010); _versionAction.Add(2012, SaveForVs2012); } private void SaveForVs2012() { Configuration.EnableSpeechRecognition = "True"; Configuration.HandGestureRecognition = AddinCommandBar.Top; }
  50. 50. 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
  51. 51. Liskov Substitution Principle Subtypes must be substitutable for their base types Inheritance and polymorphism
  52. 52. public class Rectangle { public int Width { get; set; } public int Height { get; set; } public int GetArea() { return Width*Height; } }
  53. 53. [TestFixture] public class RectangleTests { [Test] public void CheckArea_PassingTest() { Rectangle r = new Rectangle(); CheckAreaOfRectangle(r); } private void CheckAreaOfRectangle(Rectangle r) { r.Width = 5; r.Height = 2; Assert.AreEqual(10, r.GetArea()); } }
  54. 54. We need a Square!
  55. 55. public class Rectangle { protected int _width; public virtual int Width { get { return _width; } set { _width = value; } } protected int _height; public virtual int Height { get { return _height; } set { _height = value; } } public int GetArea() { return Width*Height; } } public class Square : Rectangle { public override int Width { get { return _width; } set { _width = value; _height = value; } } public override int Height { get { return _height; } set { _height = value; _width = value; } } }
  56. 56. [TestFixture] public class RectangleTests { [Test] public void CheckArea_PassingTest() { Rectangle r = new Rectangle(); CheckAreaOfRectangle(r); } private void CheckAreaOfRectangle(Rectangle r) { r.Width = 5; r.Height = 2; Assert.AreEqual(10, r.GetArea()); } [Test] public void CheckArea_FAILINGTest() { Rectangle r = new Square(); CheckAreaOfRectangle(r); } }
  57. 57. public class Rectangle { public int Width { get; set; } public int Height { get; set; } public int GetArea() { return Width * Height; } } public class Square { public int Side { get; set; } public int GetArea() { return Side * Side; } }
  58. 58. Interface Segregation Principle Don’t be force to implement unused methods Avoid “Fat Interfaces” Clients should not be forced to depend on methods they do not use
  59. 59. public override bool ValidateUser(string usercode, string password) { var returnValue = false; MoneyService moneyServices = new MoneyService(); if (moneyServices.IsValid(usercode, password)) { returnValue = true; } return returnValue; } -- snip snip snip ---- public class MoneyMembershipProvider : MembershipProvider {
  60. 60. namespace System.Web.Security { public abstract class MembershipProvider : ProviderBase { public abstract bool EnablePasswordRetrieval { get; } public abstract bool EnablePasswordReset { get; } public abstract bool RequiresQuestionAndAnswer { get; } public abstract string ApplicationName { get; set; } public abstract int MaxInvalidPasswordAttempts { get; } public abstract int PasswordAttemptWindow { get; } public abstract bool RequiresUniqueEmail { get; } public abstract MembershipPasswordFormat PasswordFormat { get; } public abstract int MinRequiredPasswordLength { get; } public abstract int MinRequiredNonAlphanumericCharacters { get; } public abstract string PasswordStrengthRegularExpression { get; } public abstract MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); public abstract bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer); public abstract string GetPassword(string username, string answer); public abstract bool ChangePassword(string username, string oldPassword, string newPassword); public abstract string ResetPassword(string username, string answer); public abstract void UpdateUser(MembershipUser user); public abstract bool ValidateUser(string username, string password); public abstract bool UnlockUser(string userName); public abstract MembershipUser GetUser(object providerUserKey, bool userIsOnline); public abstract MembershipUser GetUser(string username, bool userIsOnline); public abstract string GetUserNameByEmail(string email); public abstract bool DeleteUser(string username, bool deleteAllRelatedData); public abstract MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords); public abstract int GetNumberOfUsersOnline(); public abstract MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords); public abstract MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords); protected virtual byte[] EncryptPassword(byte[] password);
  61. 61. public class AuctionsPlusMembershipProvider : MembershipProvider { -- snip snip snip ---- public override bool ChangePassword(string username, string oldPassword, string newPassword) { throw new NotImplementedException(); } public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPa { throw new NotImplementedException(); } public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passw { throw new NotImplementedException(); } public override bool DeleteUser(string username, bool deleteAllRelatedData) { throw new NotImplementedException(); } public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecord { throw new NotImplementedException(); } public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalReco { throw new NotImplementedException(); } public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords) { throw new NotImplementedException(); }
  62. 62. public interface IEnableUservalidation { bool ValidateUser(string username, string password); } public interface IAllowUserRetrieval { MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords); MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords); MembershipUser GetUser(object providerUserKey, bool userIsOnline); MembershipUser GetUser(string username, bool userIsOnline); string GetUserNameByEmail(string email); MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords); int GetNumberOfUsersOnline(); } public interface IProvidePassword { bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer); bool ChangePassword(string username, string oldPassword, string newPassword); string ResetPassword(string username, string answer); string GetPassword(string username, string answer); }
  63. 63. Dependency Inversion Principle Depend on Abstractions Interfaces, not concrete types Inject Dependencies into Classes Inversion of Control Hollywood Principle: "Don't call us, We call you" I tell an object its partners, and not the object chooses its partners
  64. 64. public class WCFSalaryService { private IDBHelper dbHelper = new SQLHelper(); private ILoggerHelper loggerHelper = new FileLogWriter(); private IAuthenticationHelper authenticationHelper = new WebServiceAuth(); private IUserUtility userHelper; private IConnections connectionHelper = new HTTPConnectionHelper(); public WCFSalaryService() { userHelper = new UserHelper(connectionHelper); userHelper.Logger = loggerHelper; dbHelper.Logger = loggerHelper; // ----- snip snip snip ---- } // ----- snip snip snip ---- }
  65. 65. private IDBHelper _dbHelper; private ILoggerHelper _loggerHelper; private IAuthenticationHelper _authenticationHelper; private IUserUtility _userHelper; private IConnections _connectionHelper; public WCFSalaryService( IDBHelper dbHelper, ILoggerHelper loggerHelper, IAuthenticationHelper authenticationHelper, IUserUtility userHelper, IConnections connectionHelper) { _dbHelper = dbHelper; _loggerHelper = loggerHelper; _authenticationHelper = authenticationHelper; _userHelper = userHelper; _connectionHelper = connectionHelper;
  66. 66. private IDBHelper _dbHelper; private ILoggerHelper _loggerHelper = new FileLogWriter(); private IAuthenticationHelper _authenticationHelper = new WebserviceAuth(); private IUserUtility _userHelper; private IConnections _connectionHelper; public WCFSalaryService( IDBHelper dbHelper, ILoggerHelper loggerHelper, IAuthenticationHelper authenticationHelper, IUserUtility userHelper, IConnections connectionHelper) { _userHelper = new UserHelper(connectionHelper); _userHelper.Logger = loggerHelper; if (authenticationHelper != null) { _authenticationHelper = authenticationHelper; } if (dbHelper != null) { _connectionHelper.DbHelper = dbHelper; _authenticationHelper.DbHelper = dbHelper; } else { _connectionHelper.DbHelper = DBHelper.Instance; _authenticationHelper.DbHelper = DBHelper.Instance; } if (loggerHelper != null) { this._loggerHelper = loggerHelper; } _userHelper.LoggerHelper = this._loggerHelper; _authenticationHelper.LoggerHelper = this._loggerHelper; _connectionHelper.LoggerHelper = this._loggerHelper; // ----- snip snip snip ---- }
  67. 67. public class SalaryScenario : NinjectModule { public override void Load() { Bind<ILoggerHelper>().To<FileLogWriter>(); Bind<IDBHelper>().To<SQLHelper>(); Bind<IAuthenticationHelper>().To<WebServiceAuth>(); Bind<IUserUtility>().To<UserUtility>(); Bind<IConnections>().To<HTTPConnectionHelper>(); } } public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { IKernel kernel = new StandardKernel (new SalaryScenario()); var logger = kernel.Get<ILoggerHelper>(); logger.LogIt("App started up"); }
  68. 68. Solid PrinciplesS.o.l.i.d. Principles
  69. 69. Adam: "What you cant measure you cant improve!" Measure clean code
  70. 70. • Dependency Diagrams (VS2010) • StyleCop • Code Analysis (VS2010) • Code Metrics (VS2010) • Nitriq Tools!
  71. 71. • Code Auditor • ReSharper / CodeRush / Refactor Pro • Atomiq • SourceMonitor • NDepend And more... More Tools!
  72. 72. No tool can replace a code review
  73. 73. "Writing code a computer can understand is science. Writing code other programmers can understand is an art." Jason Gorman Its not easy
  74. 74. From now on...
  75. 75. Readable Code
  76. 76. Tests
  77. 77. Avoid Duplication
  78. 78. • Readable • Tests in place • No duplication "Wartung" What is clean code?
  79. 79. • Why code matters • Good and Bad Code • The Broken Window Theory • The Grand Redesign in the Sky • The Boy Scout Rule • OOP Patterns and Principles • SOLID Principles • How to measure clean code? •Tools Summary
  80. 80. Further Reading
  81. 81. Further Reading
  82. 82. Further Reading
  83. 83. Further Reading
  84. 84. http://www.clean-code-developer.de/
  85. 85. VS2010 Code Metrics http://bit.ly/bda4T1 JB Rainsberger The Four Elements of Simple Design http://www.jbrains.ca/permalink/the-four-elements-of-simple-design How to hire a programmer? Have people fix up some smelly code http://codebetter.com/blogs/karlseguin/archive/2006/12/01/How-to-hire-a-programmer- _2D00_-Part-2-_2D00_-Improve-this-code.aspx C# Coding Practices http://www.codeproject.com/KB/cs/CSharp_Coding_Practices.aspx Object Oriented Principles http://www.objectmentor.com/omSolutions/oops_what.html Further Reading
  86. 86. http://www.refactoring.com/ http://refactormycode.com/ All links and slides on http://blog.gfader.com/ Further Doing
  87. 87. VS2010 http://msdn.microsoft.com/en-us/vstudio/ Nitriq & Atomiq http://nimblepros.com/products.aspx SourceMonitor http://www.campwoodsw.com/sourcemonitor.html Ndepend http://www.ndepend.com/ Tools References
  88. 88. Better Software An introduction to good code Giordano Scalzo, 06/05/2009 Thanks to Giordano!
  89. 89. http://creativecommons.org/licenses/by-nc-sa/3.0/
  90. 90. Be a boy scout Leave the campground cleaner than you found it
  91. 91. Be a boy scout Leave code cleaner than you found it All links and slides on http://blog.gfader.com Thank you!!!

×