69
Fernando Escolar @fernandoescolar [email protected] Code Smells Y A X B

DotNet Conference: code smells

Embed Size (px)

Citation preview

Page 1: DotNet Conference: code smells

Fernando [email protected]@tokiota.com

Code Smells

Y

AX B

Page 2: DotNet Conference: code smells
Page 3: DotNet Conference: code smells
Page 4: DotNet Conference: code smells
Page 5: DotNet Conference: code smells
Page 6: DotNet Conference: code smells
Page 7: DotNet Conference: code smells
Page 8: DotNet Conference: code smells
Page 9: DotNet Conference: code smells
Page 10: DotNet Conference: code smells

public void CalculateTotalPrice(){

//calculates taxesvar taxes = 0.0m;foreach (var product in this.productLines){

// ...taxes += product.Price / product.TaxesPercent * 100;

}

//calculates pricevar price = 0.0m;foreach (var product in this.productLines){

// ...price += product.Price * product.Unit;

}

this.TotalPrice = price + taxes;}

Page 11: DotNet Conference: code smells

public void CalculateTotalPrice(){

var taxes = this.CalculateTaxes();var price = this.CalculatePrice();

this.TotalPrice = price + taxes;}

public decimal CalculateTaxes(){

// ...return taxes;

}

public decimal CalculatePrice(){

// ...return price;

}

Page 12: DotNet Conference: code smells
Page 13: DotNet Conference: code smells

public class User{

// ...}public class Product{

// ...}public class ProductLine{

// ...}public class Order{

// ...}

Page 14: DotNet Conference: code smells

public class Customer{

private string name;private string lastName;private string streetType;private string streetName;private string streetNumber;private string floorNumber;private string doorNumber;private string postalCode;private string city;private string state;private string country;private string phone;private string email;

}

Page 15: DotNet Conference: code smells

public class Customer {private string name;private string lastname;private string phone;private string email;private Address address;

}

public class Address {private string streetType;private string streetName;private string streetNumber;private string floorNumber;private string doorNumber;private string postalCode;private string city;private string state;private string country;

}

Page 16: DotNet Conference: code smells

public void Draw(int x, int y, int width, int heigth, int borderSize, Color borderColor, Color backgroundColor)

{ // ...

}

Page 17: DotNet Conference: code smells

public void Draw(BorderedRectangle rectangle){

// ...}

public class Rectangle{

public int X { get; set; }public int Y { get; set; }public int Width { get; set; }public int Height { get; set; }public Color Color { get; set; }

}

public class BorderedRectangle : Rectangle{

public int BorderSize { get; set; }public Color BorderColor { get; set; }

}

Page 18: DotNet Conference: code smells

class Rectangle{

public void Draw(int x, int y, int width, int height, int borderSize, Color borderColor, Color backgroundColor)

{this.DrawRectable(x, y, width, height, backgroundColor);this.DrawRectangleBorder(x, y, width, height, borderSize,

borderColor);}private void DrawRectable(int x, int y, int width, int height,

Color color){

// ...}private void DrawRectangleBorder(int x, int y, int width,

int height, int borderSize, Color color){

// ...}

Page 19: DotNet Conference: code smells

public class Rectangle{

public int X { get; set; }public int Y { get; set; }public int Width { get; set; }public int Height { get; set; }public Color Color { get; set; }

public void Draw() { /* ... */ }}

public class BorderedRectangle : Rectangle{

public int BorderSize { get; set; }public Color BorderColor { get; set; }

public void Draw() { base.Draw(); /* ... */ }}

Page 20: DotNet Conference: code smells

namespace MyTech{

class MyTechConnection { }class MyTechConnectionHndl { }

class Order {public bool Order(OrderRequest request) { /* ... */ }

}

class Inv{

int Do(int a, string s, double d) { /* ... */ }}

}

Page 21: DotNet Conference: code smells

namespace MyTechnology{

class Connection { }class ConnectionHandler { }

class OrderManager {public bool Request(Order order) { /* ... */ }

}

class Invoice{

bool Pay(int userId, string account, double price) { /* ... */

}}

}

Page 22: DotNet Conference: code smells
Page 23: DotNet Conference: code smells

private const decimal USDolarChange = 1.13845m;private const decimal BritishPoundsChange = 0.739373275m;public enum MoneyTypes { Euro, USDolar, BritishPounds };

public decimal ConvertValue(decimal money, MoneyTypes type){

switch (type){

case MoneyTypes.Euro:return money;

case MoneyTypes.USDolar:return money * USDolarChange;

case MoneyTypes.BritishPounds:return money * BritishPoundsChange;

default:return money;

}}

Page 24: DotNet Conference: code smells

public class Euro {public decimal Value { get; set; }public virtual decimal GetConvertedValue(){

return this.Value;}

}public class USDolar : Euro {

public override decimal GetConvertedValue(){

return this.Value * USDolarChange;}

}public class BritishPounds : Euro {

public override decimal GetConvertedValue(){

return this.Value * BritishPoundsChange;}

}

Page 25: DotNet Conference: code smells

public decimal GetDiscount(){

var basePrice = quantity * itemPrice;if (quantity > 1000)

return basePrice * 0.95;else if (quantity > 500)

return 20.0d;}

Page 26: DotNet Conference: code smells

public decimal GetDiscount(){

if (quantity > 1000)return CalculateBasePrice() * 0.95;

else if (quantity > 500)return 20.0d;

}private decimal CalculateBasePrice(){

return quantity * itemPrice;}

Page 27: DotNet Conference: code smells

abstract class Membership{

public abstract IEnumerable<User> GetUsers();public abstract IEnumerable<Profile> GetProfiles();public abstract User Auth(string user, string password);public abstract IEnumerable<Profile> GetUserProfile(User user);

}class MyMembership : Membership{

public override IEnumerable<User> GetUsers() { throw new NotImplementedException(); }

public override IEnumerable<Profile> GetProfiles() { throw new NotImplementedException(); }

public override User Auth(string user, string password) {// ...

}public override IEnumerable<Profile> GetUserProfile(User user) {

// ...}

}

¿Liskov?

Page 28: DotNet Conference: code smells

abstract class Membership{

public abstract User Auth(string user, string password);public abstract IEnumerable<Profile> GetUserProfile(User user);

}

class MyMembership : Membership{

public override User Auth(string user, string password){

// ...}public override IEnumerable<Profile> GetUserProfile(User user) {

// ...}

}

Page 29: DotNet Conference: code smells

class TokiotaService{

// ...}class MicrosoftService{

// ...}

public IEnumerable<TokiotaItem> GetTokiotaItems(){

return this.tokiotaService.GetProyects();}

public IEnumerable<MicrosoftItem> GetMicrosoftItems(){

return this.microsoftService.GetPartnerCollaborations();}

Page 30: DotNet Conference: code smells

class Item { /* ... */ }

class IAdapter{

IEnumerable<Item> GetItems();}

class MicrosoftAdapter : IAdapter { /* ... */ }

class TokiotaAdapter : IAdapter { /* ... */ }

public IEnumerable<Item> GetItems(IAdapter adapter){

return adapter.GetItems();}

Page 31: DotNet Conference: code smells

public decimal CalculateTotalPrice() {if (this.isChristmas) {

return this.CalculateChristmas();}else {

if (this.HasDiscount()) {return this.CalculateDiscounted(this.GetDiscount());

}else {

if (this.isTimeOfSale) {return this.CalculateDiscounted(this.GetSalesDiscount());

}else{return this.CalculateStandard();

}}

}}

Page 32: DotNet Conference: code smells

public decimal CalculateTotalPrice(){if (this.isChristmas)

return this.CalculateChristmas();

if (this.HasDiscount()) return this.CalculateDiscounted(this.GetDiscount());

if (this.isTimeOfSale) return this.CalculateDiscounted(this.GetSalesDiscount());

return this.CalculateStandard();}

Page 33: DotNet Conference: code smells
Page 34: DotNet Conference: code smells

class Customer{

public string Name { get; set; }public string LastName { get; set; }public string toXML(){

return @"<Customer>" + "<Name>" +

this.Name + "</Name>" +"<LastName>" +

this.LastName +"</LastName>" +

"</Customer>";}

}

Open-Close?

Page 35: DotNet Conference: code smells

class Customer{

private readonly CustomerXmlSerializer serializer = ...;public string Name { get; set; }public string LastName { get; set; }public string toXML(){

return this.serializer.Serialize(this);}

}class CustomerXmlSerializer : XmlSerializer{

public string Serialize(Customer c){

var sb = new StringBuilder();sb.Append(this.StartTag("Customer"));sb.Append(this.WriteTag("Name", c.Name));sb.Append(this.WriteTag("LastName", c.LastName));sb.Append(this.EndTag("Customer"));

}}

Page 36: DotNet Conference: code smells

class OrderManager{

decimal CalculateOrderPrice(Product[] products, int[] units) { /* ... */

}}

class OrderViewModel{

public Product[] Products { get; set; }public int[] Units { get; set; }

}

class OrderService{

void SendOrder(Product[] products, int[] units) { /* ... */ }}

¿Y si queremos añadir IVA?

Page 37: DotNet Conference: code smells

class Order{

public Product[] Products { get; set; }public int[] Units { get; set; }public decimal GetPrice() { /* ... */ }

}class OrderManager{

decimal CalculateOrderPrice(Order order) { return order.GetPrice();

}}class OrderViewModel{

public Order Order { get; set; }}class OrderService{

void SendOrder(Order order) { /* ... */ }}

Page 38: DotNet Conference: code smells

class Vehicle { }

class Car : Vehicle { }

class Truck : Vehicle { }

class Motorbike : Vehicle { }

class VehicleXmlSerializer { }

class CarXmlSerializer : VehicleXmlSerializer { }

class TruckXmlSerializer : VehicleXmlSerializer { }

class MotorbikeXmlSerializer : VehicleXmlSerializer { }

Vehicle

Class

Car

Truck

Motorbike

Xml

Car

Truck

MotorBike

Page 39: DotNet Conference: code smells

class VehicleXmlSerializer{

public VehicleXmlSerializer(IToXmlStrategy[] strategies) { }}

interface IToXmlStrategy { }

interface IToXmlStrategy<T> where T : Vehicle { }

class CarToXmlStrategy : IToXmlStrategy<Car> { }

class TruckTOXmlStrategy : IToXmlStrategy<Truck> { }

class MotorbikeToXmlStrategy : IToXmlStrategy<Motorbike> { }

Page 40: DotNet Conference: code smells

class OrderViewModel{

decimal CalculateTax() { return totalPrice * 0.21; }}

class Invoice{

decimal ShowTax() { return order.TotalPrice * 0.21; }}

class ViewModel{

public decimal Tax { get { return 21.0; } } }

Page 41: DotNet Conference: code smells

static class Globals{

public const decimal TaxPercent = 21.0m;public const decimal TaxDelta = TaxPercent / 100;

}

class OrderViewModel{

decimal CalculateTax() { return totalPrice * Globals.TaxDelta; }}

class Invoice{

decimal ShowTax() { return order.TotalPrice * Globals.TaxDelta; }}

class ViewModel{

public decimal Tax { get { return Globals.TaxPercent; } }}

Page 42: DotNet Conference: code smells
Page 43: DotNet Conference: code smells

class LazyClass{

public static string FormatName(string name, string lastname, string surname)

{return string.Format("{1}, {0} ({2})",

name, lastname, surname);}

}

Solo un método llamada desde solo un lugar

Page 44: DotNet Conference: code smells

class CustomerInfo{

public string Name { get; set; }public string LastName { get; set; }public string Surname { get; set; }

public override string ToString(){

return string.Format("{1}, {0} ({2})", this.Name, this.LastName, this.Surname);

}}

Page 45: DotNet Conference: code smells

class Settings{

public string Name { get; set; }public string Host { get; set; }public bool UseSsl { get; set; }public int NumerOfConnections { get; set; }

}

class SettingsManager{

public void Save(Settings settings) { /* ... */ }public Settings Load() { /* ... */ }

}

Una clase con las propiedades y otra con los métodos

Page 46: DotNet Conference: code smells

class Settings{

public string Name { get; set; }public string Host { get; set; }public bool UseSsl { get; set; }public int NumerOfConnections { get; set; }

public void Save() { /* ... */ }public void Load() { /* ... */ }

}

Page 47: DotNet Conference: code smells

class MyClass{

public int MyProperty { get; set; }public string Name { get; set; }

public void Process() { /* ... */ }public void Send() { /* ... */ }

}

class MyClassOld{

public string Name { get; set; }

public void Process() { /* ... */ }}

Page 48: DotNet Conference: code smells

class MyClass{

public int MyProperty { get; set; }public string Name { get; set; }

public void Process() { /* ... */ }public void Send() { /* ... */ }

}

Page 49: DotNet Conference: code smells

class CustomerController {public ActionResult Edit(Customer customer) {

if (Model.IsValid) {this.customerRepository.InsertOrUpdate(customer);this.customerRepository.Save();return RedirectToAction("Index");

}return View();

}}class EmployeeController {

public ActionResult Edit(Employee employee) {if (Model.IsValid) {

this.employeeRepository.InsertOrUpdate(employee);this.employeeRepository.Save();return RedirectToAction("Index");

}return View();

}}

Page 50: DotNet Conference: code smells

class ControllerBase<TEntity> where TEntity : IEntity{

IRepository<Employee> repository;

public ActionResult Edit(TEntity entity) {

if (Model.IsValid) {

this.repository.InsertOrUpdate(entity);this.repository.Save();return RedirectToAction("Index");

}

return View();}

}

class CustomerController : ControllerBase<Customer> { }

class EmployeeController : ControllerBase<Employee> { }

Page 51: DotNet Conference: code smells

public interface ITextFormatStrategy { string Format(string input); }

public class CommentTextFormatContext {private ITextFormatStrategy strategy;

public CommentTextFormatContext(ITextFormatStrategy strategy) {this.strategy = strategy;

}

public int ExecuteStrategy(string input) {return strategy.Format(input);

}}

public class RemoveHtmlTagsStrategy : ITextFormatStrategy {public string Format(string input) {

return Regex.Replace(input, @"<[^>]*>", string.Empty);}

}

¿Solo una estrategia? YAGNI

Page 52: DotNet Conference: code smells

public class Comment{

public void SetMessage(string message){

this.Text = Regex.Replace(message, @"<[^>]*>", string.Empty);}

}

Page 53: DotNet Conference: code smells

// calculates the total price of the orderpublic decimal Calculate(){

// in christmas timeif (DateTime.Now >= new DateTime(DateTime.Now.Year, 12, 25)

&& DateTime.Now <= new DateTime(DateTime.Now.Year + 1, 1, 5)){

// it has a discount of 10%return this.TotalPrice * 0.9;

}else{

return thisw.TotalPrice;}

}

Page 54: DotNet Conference: code smells

public decimal CalculateOrderTotalPrice(){

if (IsChirstmasTime()) {return this.ApplyDiscountPercentage(10);

}else {

return this.TotalPrice;}

}

private decimal ApplyDiscountPercentage(int percentage){

return this.TotalPrice * (100 - percentage) / 100;}

private static bool IsChirstmasTime(){

return DateTime.Now >= new DateTime(DateTime.Now.Year, 12, 25) && DateTime.Now <= new DateTime(DateTime.Now.Year + 1, 1, 5);

}

Page 55: DotNet Conference: code smells
Page 56: DotNet Conference: code smells

public class Product{

public bool HasBeenOrdered(Order order){

return order.Products.Contains(this);}

}

public class Order{

public List<Product> Products { get; set; }}

Page 57: DotNet Conference: code smells

public class Product { }

public class Order{

public List<Product> Products { get; set; }

public bool HasBeenOrdered(Product product){

return this.Products.Contains(product);}

}

Page 58: DotNet Conference: code smells

public class Customer{

public string Name { get; set; }public string LastName { get; set; }public string PhoneNumber { get; set; }public string Address { get; set; }

}public class Order{

private Customer customer;public string GetSummary(){

var summary = string.Format("{0} {1}\n{2}\n{3}", this.customer.Name, this.customer.LastName,this.customer.PhoneNumber,this.customer.Address);

summary += "\n" + this.TotalPrice + " €";return summary;

}}

Page 59: DotNet Conference: code smells

public class Customer{

public string Name { get; set; }public string LastName { get; set; }public string PhoneNumber { get; set; }public string Address { get; set; }public override string ToString() {

return string.Format("{0} {1}\n{2}\n{3}",this.Name,this.LastName,this.PhoneNumber,this.Address);

}}public class Order{

private Customer customer;public string GetSummary() {return this.customer.ToString() + "\n" + this.TotalPrice + " €";}

}

Page 60: DotNet Conference: code smells

public decimal CalculateTotalPrice(){

/* ... */if (this.Customer.Address.Country.IsEuropean){

/* ... */}

/* ... */}

Page 61: DotNet Conference: code smells

public decimal CalculateTotalPrice(){

/* ... */if (this.Customer.IsEuropean){

/* ... */}

/* ... */}

public class Customer{

public bool IsEuropean{

get{

return this.Address.IsEuropean;}

}}

Page 62: DotNet Conference: code smells

class MyConnection{

private SqlConnection connection = new SqlConnection();

public string ConnectionString{

get { return this.connection.ConnectionString; } }

public void Open(){

this.connection.Open();}

public MyCommand CreateCommand(){

return new MyCommand(this.connection.CreateCommand());}

}

Page 63: DotNet Conference: code smells

SqlConnection connection = new SqlConnection();

Page 64: DotNet Conference: code smells

class XmlSerializer{

public string StartTagString { get; set; }public string EndTagString { get; set; }

public XmlSerializer(){

StartTagString = "<{0}>";EndTagString = "</{0}>";

}protected string WriteTag(string name, string value) {

return StartTag(name) + value + EndTag(name); }protected string StartTag(string name) {

return string.Format(StartTagString, name); }protected string EndTag(string name) {

return string.Format(EndTagString, name); }

}

Page 65: DotNet Conference: code smells

class XmlSerializer{

private const string StartTagString = "<{0}>";private const string EndTagString = "</{0}>";

protected string WriteTag(string name, string value) {

return StartTag(name) + value + EndTag(name); }

protected string StartTag(string name) {

return string.Format(StartTagString, name); }

protected string EndTag(string name) {

return string.Format(EndTagString, name); }

}

Page 66: DotNet Conference: code smells
Page 67: DotNet Conference: code smells
Page 68: DotNet Conference: code smells
Page 69: DotNet Conference: code smells

Fernando [email protected]@tokiota.com

¡¡¡Si te ha gustado no olvidesrellenar la encuesta!!!Thanks

Y

AX B