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
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. • 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
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?
39. "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?
43. 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;
// ...
}
44. 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) { //...
}
}
45. OpenClose Principle
Open for extension
Close for modification
Every entity should be open for extension, but closed for modification
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);
}
public void SaveToolbarStateBetter()
{
var version = Core.GetVisualStudioVersion();
if (_versionAction.ContainsKey(version))
{
_versionAction[version].Invoke();
}
}
50. 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;
}
51. 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
56. 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;
}
}
}
57. [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);
}
}
58. 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;
}
}
59. 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
60. 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
{
61. 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);
62. 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();
}
63. 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);
}
64. 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
65. 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 ----
}
69. 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");
}
81. • Readable
• Tests in place
• No duplication
"Wartung"
What is clean code?
82. • 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
88. 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
93. Be a boy scout
Leave the campground cleaner than you found it
94. Be a boy scout
Leave code cleaner than you found it
All links and slides on
http://blog.gfader.com
Thank you!!!
Editor's Notes
http://blog.gfader.com
Click to add notes...Peter Gfader http://blog.gfader.com
Z1 1936ENIAC 1943, ..
Z1 1936ENIAC 1943, ..
Z1 1936ENIAC 1943, ..
Z1 1936ENIAC 1943, ..
Adams theory on this is: “We will have an exponential growth as soon as code is writing code”
Open Word and get suggestions!CorrectnessUsabilityEfficiencyReliabilityIntegrityAdaptabilityAccuracyRobustness Maintainability FlexibilityPortabilityReusabilityReadabilityTestabilityUnderstandabilityNot too many WTFsEasy to readEasy to maintainConsistent convention (naming, layout, design patterns)Follows SSW rulesCode Auditor 0No bugsDoes what it should do (Client’s needs)Easy to changeWritten in a language, that you can find dev's forPerformance
Who is a dev? Testers? BA's? PM's?
This is a VB programmer, so he probably needs that
HTML spew, I don’t say more… beginner devs underneath covers
Less code = less code to read?Shorter code = faster?Shorter code = less memory?
To get things done?Speed?Meeting Deadlines?Taking Shortcuts..But after too many shortcuts there is no progress...
DBASE was terrible!Look up wikipedia
Sushi Chef rule - Clean up as you doThe Sushi Chef has done this certain routine of hand movements already 100 times before.He is just following this routine.He cleans up as he does his jobs.#1 Software devs do something slightly different every day#2 Devs want to get something done. The brain is in "Get done mode". Clean up later
What is code?Stuff that a machine reads
High cohesion - better understandability, robustnessLow coupling - better maintainability, high resistance to changes