Refactoring’i Detaylı Bir Örnek İle İnceleme Part 2
Selamlar,
Bu makalede, Refactoring serisine kaldığımız yerden devam edeceğiz. Part 1’e buradan erişebilirsiniz.
Kodun Fonksiyonlara Dağtılması : Kodun son hali aşağıdaki gibidir. Şu anki yapı, ilk haline göre çok daha dağıtık ve anlaşılırdır. Tüm işler fonksiyonlara taşınmıştır. Ana methodda yapılacak tek iş, ilgili fonksiyonun çağrılmasıdır.
- getAmounth()
- findCourse()
- calculateVolumeCredit()
- calculateVolumeCredit()
- tr()
- totalValumeCredits()
- getTotalAmount()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace Refactoring { class Program { static IDictionary<string, Course> courses; static Invoice invoice; static void Main(string[] args) { Console.WriteLine("Wellcome to Refactoring Example"); courses = new Dictionary<string, Course>(); courses.Add("dpattern", new Course() { Name = "Design Pattern", Type = Types.Software }); courses.Add("hface", new Course() { Name = "Human Face", Type = Types.Art }); courses.Add("redis", new Course() { Name = "Redis", Type = Types.Software }); invoice = new Invoice(); invoice.customerName = "Hasel Team"; invoice.registers = new Register[]{ new Register(){courseID="dpattern",student=20}, new Register() { courseID = "hface", student = 15 }, new Register() { courseID = "redis", student = 5 }, }; var result = $"{invoice.customerName} için Fatura Detayı: \n"; foreach (Register reg in invoice.registers) { result += $"{findCourse(reg).Name}: {tr(getAmounth(reg) / 100)} ({reg.student} kişi)\n"; } result += $"Toplam borç { tr(getTotalAmount() / 100) }\n"; result += $"Kazancınız { tr(totalValumeCredits()) } \n"; Console.WriteLine(result); Console.ReadLine(); } public static int getAmounth(Register register) { var result = 0; switch (findCourse(register).Type) { case Types.Art: { result = 3000; if (register.student > 15) { result += 1000 * (register.student - 10); } break; } case Types.Software: { result = 30000; if (register.student > 10) { result += 10000 + 500 * (register.student - 5); } result += 300 * register.student; break; } } return result; } public static Course findCourse(Register register) { return courses[register.courseID]; } public static decimal calculateVolumeCredit(Register register) { decimal volumeCredits = 0; //kazanılan para puan volumeCredits += Math.Max(register.student - 15, 0); // extra bonus para puan her 5 yazılım öğrencisi için decimal fiveStudentGroup = register.student / 5; if (Types.Software == findCourse(register).Type) volumeCredits += Math.Floor(fiveStudentGroup); return volumeCredits; } public static string tr(decimal value) { CultureInfo trFormat = new CultureInfo("tr-TR", false); trFormat.NumberFormat.CurrencySymbol = "TL"; trFormat.NumberFormat.NumberDecimalDigits = 2; return value.ToString("C", trFormat); } public static decimal totalValumeCredits() { decimal result = 0; foreach (Register reg in invoice.registers) { result += calculateVolumeCredit(reg); } //return volumeCredits; return result; } public static decimal getTotalAmount() { // decimal totalAmount = 0; decimal result = 0; foreach (Register reg in invoice.registers) { //totalAmount += getAmounth(reg); result += getAmounth(reg); } //return totalAmount; return result; } } } |
Hesaplamaları ve Formatı Aşamalara Bölme:
Bu zamana kadar karmaşık parçaları, küçük parçalara ayırıp fonksiyonlar yardımı ile çağırdık. Bu aşamadan sonra, daha çok işlevselliğin değişimi üzerine konuşacağız.
Şimdi gelin tüm methodları, CalculateAmounth() methodu altında topluyalım. Ayrı bir method altına toplamaya Extract Function, method içinde method tanımlamaya Inline Function, bunu C# karşılığına da Local Function denilmektedir.
Main(): CalculateAmount() methodunun çağrıldığı ana methoddur. C# Console Aplication’ın çalıştırıldığı an, default çağrılan static methoddur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
static IDictionary<string, Course> courses; static Invoice invoice; static void Main(string[] args) { Console.WriteLine("Wellcome to Refactoring Example"); courses = new Dictionary<string, Course>(); courses.Add("dpattern", new Course() { Name = "Design Pattern", Type = Types.Software }); courses.Add("hface", new Course() { Name = "Human Face", Type = Types.Art }); courses.Add("redis", new Course() { Name = "Redis", Type = Types.Software }); invoice = new Invoice(); invoice.customerName = "Hasel Team"; invoice.registers = new Register[]{ new Register(){courseID="dpattern",student=20}, new Register() { courseID = "hface", student = 15 }, new Register() { courseID = "redis", student = 5 }, }; CalculateAmounth(invoice, courses); } |
CalculateAmounth(): Tüm methodların toplandığı, global methoddur. Böylece, kod parçalanarak fonksiyonlara dağtılmış olsa da, tüm işler tek bir yerden yönetlebilmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static void CalculateAmounth(Invoice invoice, IDictionary<string, Course> courses) { var result = $"{invoice.customerName} için Fatura Detayı: \n"; foreach (Register reg in invoice.registers) { result += $"{findCourse(reg).Name}: {tr(getAmounth(reg) / 100)} ({reg.student} kişi)\n"; } result += $"Toplam borç { tr(getTotalAmount() / 100) }\n"; result += $"Kazancınız { tr(totalValumeCredits()) } \n"; Console.WriteLine(result); Console.ReadLine(); Course findCourse(Register register){ ... } decimal calculateVolumeCredit(Register register) { ... } string tr(decimal value){ ... } decimal totalValumeCredits(){ ... } decimal getTotalAmount(){ ... } int getAmounth(Register register){ ... } } |
PassingData : Ara veri taşıyan bir sınıftır. Kısaca Ana methodda (CalculateAmounth())’a paramtere taşımak amaçlı kullanılan, ara bir sınıftır.
1 2 3 4 |
public class PassingData{ public string customerName { get; set; } public Register[] registers { get; set; } } |
Image Source: https://refactoringguru.cn/images/refactoring/content/smells/long-parameter-list-01-2x.png
Yine aşağıda, “CalculateAmounth()” methodunun işleyişinin 2 faza bölünmesi amaçlanmıştır. 2 faz arasında, ara veri taşıyan bir nesne yaratılmıştır. Bu nesne yukarıda tanımlanan PassingData sınıfıdır.
static void Main(string[] args){.PassingDatapassingData=new PassingData();CalculateAmounth(passingData, invoice, courses);}public static void CalculateAmounth(PassingData data, Invoice invoice, IDictionary<string, Course> courses){.}
static void Main(string[] args){.PassingData passingData=new PassingData();passingData.customerName = invoice.customerName;CalculateAmounth(passingData, invoice, courses);}
public static void CalculateAmounth(PassingData data, Invoice invoice, IDictionary<string, Course> courses){/* var result = $”{invoice.customerName} için Fatura Detayı: \n”; */var result=$”{data.customerName} için Fatura Detayı: \n”;.}
static void Main(string[] args){.PassingData passingData=new PassingData();passingData.customerName = invoice.customerName;passingData.registers=invoice.registers;CalculateAmounth(passingData,invoice, courses);}
public static void CalculateAmounth(PassingData data,Invoice invoice, IDictionary<string, Course> courses){var result=$”{data.customerName} için Fatura Detayı: \n”;/* foreach (Register reg in invoice.registers) */foreach (Register reg in data.registers){result+=$”{findCourse(reg).Name}: {tr(getAmounth(reg) /100)} ({reg.student} kişi)\n”;}decimal totalValumeCredits(){decimalresultValume=0;/* foreach (Register reg in invoice.registers) */foreach (Register reg in data.registers){resultValume+=calculateVolumeCredit(reg);}return resultValume;}decimal getTotalAmount(){decimal resultTotalAmounth=0;/* foreach (Register reg in invoice.registers) */foreach (Register reg in data.registers){resultTotalAmounth+=getAmounth(reg);}return resultTotalAmounth;}..}
static class Program{public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item=> (Register)item.Clone()).ToArray();}
static void Main(string[] args){.PassingData passingData=new PassingData();passingData.customerName = invoice.customerName;/*passingData.registers=invoice.registers;*/passingData.registers = invoice.registers.CloneRegister();CalculateAmounth(passingData, courses);}..}
using System;public class Register : ICloneable{publicstring courseID { get; set; }publicint student { get; set; }publicobject Clone(){return this.MemberwiseClone();}public Course course { get; set; }}
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{Register reg=(Register)item.Clone();reg.course=findCourse(reg);return reg;}).ToArray();}
public static Course findCourse(Register register){return courses[register.courseID];}
public static void CalculateAmounth(PassingData data, IDictionary<string, Course> courses){var result=$”{data.customerName} için Fatura Detayı: \n”;foreach (Register reg in data.registers){/* result += $”{findCourse(reg).Name}: {tr(getAmounth(reg) / 100)} ({reg.student} kişi)\n”; */result+=$”{reg.course.Name}: {tr(getAmounth(reg) /100)} ({reg.student} kişi)\n”;}result+=$”Toplam borç { tr(getTotalAmount() /100) }\n”;result+=$”Kazancınız { tr(totalValumeCredits()) } \n”;Console.WriteLine(result);Console.ReadLine();decimal calculate VolumeCredit(Register register){decimal volumeCredits=0;//kazanılan para puanvolumeCredits+=Math.Max(register.student-15, 0);// extra bonus para puan her 5 yazılım öğrencisi içindecimal fiveStudentGroup=register.student/5;/* if (Types.Software == findCourse(register).Type) volumeCredits += Math.Floor(fiveStudentGroup); */if (Types.Software == register.course.Type) volumeCredits+=Math.Floor(fiveStudentGroup);return volumeCredits;}string tr(decimal value){CultureInfo trFormat=new CultureInfo(“tr-TR”, false);trFormat.NumberFormat.CurrencySymbol=”TL”;trFormat.NumberFormat.NumberDecimalDigits=2;return value.ToString(“C”, trFormat);}decimal totalValumeCredits(){decimal resultValume=0;foreach (Register reg in data.registers){resultValume+=calculateVolumeCredit(reg);}return resultValume;}decimal getTotalAmount(){decimal resultTotalAmounth=0;foreach (Register reg in data.registers){resultTotalAmounth+=getAmounth(reg);}return resultTotalAmounth;}int getAmounth(Register register){var resultAmounth=0;/* switch (findCourse(register).Type) */switch (register.course.Type){case Types.Art:{resultAmounth=3000;if (register.student>15){resultAmounth += 1000 * (register.student-10);}break;}case Types.Software:{resultAmounth=30000;if (register.student>10){resultAmounth+=10000+500 * (register.student-5);}resultAmounth+=300 * register.student;break;}}return resultAmounth;}}
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{Register reg=(Register)item.Clone();reg.course=findCourse(reg);reg.amount=getAmounth(reg);return reg;}).ToArray();}
using System;public class Register : ICloneable{publicstring courseID { get; set; }publicint student { get; set; }publicobject Clone(){return this.MemberwiseClone();}public Course course { get; set; }public int amount { get; set; }}
public static int getAmounth(Register register) { … }
public static void CalculateAmounth(PassingData data, IDictionary<string, Course> courses){var result=$”{data.customerName} için Fatura Detayı: \n”;foreach (Register reg in data.registers){/*result+=$”{reg.course.Name}: {tr(getAmounth(reg) /100)} ({reg.student} kişi)\n”;*/result += $”{reg.course.Name}: {tr(reg.amount/100)} ({reg.student} kişi)\n”;}result+=$”Toplam borç { tr(getTotalAmount() /100) }\n”;result+=$”Kazancınız { tr(totalValumeCredits()) } \n”;Console.WriteLine(result);Console.ReadLine();decimal calculateVolumeCredit(Register register){decimal volumeCredits=0;//kazanılan para puanvolumeCredits+=Math.Max(register.student-15, 0);// extra bonus para puan her 5 yazılım öğrencisi içindecimal fiveStudentGroup=register.student/5;if (Types.Software == register.course.Type) volumeCredits+=Math.Floor(fiveStudentGroup);return volumeCredits;}string tr(decimal value){CultureInfo trFormat=new CultureInfo(“tr-TR”, false);trFormat.NumberFormat.CurrencySymbol=”TL”;trFormat.NumberFormat.NumberDecimalDigits=2;return value.ToString(“C”, trFormat);}decimal totalValumeCredits(){decimal resultValume=0;foreach (Register reg in data.registers){resultValume+=calculateVolumeCredit(reg);}return resultValume;}decimal getTotalAmount(){decimal resultTotalAmounth=0;foreach (Register reg in data.registers){/* resultTotalAmounth+=getAmounth(reg);*/resultTotalAmounth += reg.amount;}return resultTotalAmounth;}}
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{Register reg=(Register)item.Clone();reg.course=findCourse(reg);reg.amount=getAmounth(reg);reg.valumeCredit=calculateVolumeCredit(reg);return reg;}).ToArray();}
using System;public class Register : ICloneable{publicstring courseID { get; set; }publicint student { get; set; }publicobject Clone(){return this.MemberwiseClone();}public Course course { get; set; }public int amount { get; set; }public decimal valumeCredit { get; set; }}
public static decimal calculateVolumeCredit(Register register) { … }
public static void CalculateAmounth(PassingData data, IDictionary<string, Course> courses){..decimal totalValumeCredits(){decimal resultValume=0;foreach (Register reg in data.registers){/*resultValume+=calculateVolumeCredit(reg);*/resultValume += reg.valumeCredit;}return resultValume;}..}
public static decimal totalVolumeCredit(PassingData data) { … }public static decimal getTotalAmounth(PassingData data) { … }
using System.Collections.Generic;public class PassingData{public string customerName { get; set; }public Register[] registers { get; set; }public decimal TotalAmount { get; set; }public decimal TotalValumeCredits { get; set; }}
static void Main(string[] args){..PassingData passingData =new PassingData();passingData.customerName = invoice.customerName;passingData.registers = invoice.registers.CloneRegister();passingData.TotalAmount = getTotalAmount(passingData);passingData.TotalValumeCredits = totalValumeCredits(passingData);CalculateAmounth(passingData, courses);}
public static void CalculateAmounth(PassingData data, IDictionary<string, Course> courses){var result =$”{data.customerName} için Fatura Detayı: \n”;foreach (Register reg in data.registers){result += $”{reg.course.Name}: {tr(reg.amount / 100)} ({reg.student} kişi)\n”;}result += $”Kazancınız { tr(totalValumeCredits()) } \n”; *//* result += $”Toplam borç { tr(getTotalAmount() / 100) }\n”;result += $”Kazancınız { tr(totalValumeCredits()) } \n”;*/result += $”Toplam borç { tr(data.TotalAmount / 100) }\n”;result += $”Kazancınız { tr(data.TotalValumeCredits) } \n”;..}
1 2 3 4 5 6 7 8 9 10 |
public class RegisterGenerator { public Register register { get; set; } public Course course { get; set; } public RegisterGenerator(Register _register, Course _course) { this.register = _register; this.course = _course; } } |
public static Register[] CloneRegister(thisRegister[] arrayToClone){return arrayToClone.Select(item =>{RegisterGenerator regGenerator =new RegisterGenerator(item, findCourse(item));Register reg = (Register)item.Clone();//reg.course = findCourse(reg);reg.course = regGenerator.course;reg.amount = getAmounth(reg);reg.valumeCredit = calculateVolumeCredit(reg);return reg;}).ToArray();}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class RegisterGenerator { public Register register { get; set; } public Course course { get; set; } public int getAmounth { get { var resultAmounth = 0; switch (this.course.Type) { case Types.Art: { resultAmounth = 3000; if (this.register.student > 15) { resultAmounth += 1000 * (this.register.student - 10); } break; } case Types.Software: { resultAmounth = 30000; if (this.register.student > 10) { resultAmounth += 10000 + 500 * (this.register.student - 5); } resultAmounth += 300 * this.register.student; break; } } return resultAmounth; } } public RegisterGenerator(Register _register, Course _course) { this.register = _register; this.course = _course; } } |
public static int getAmounth(Register register){return new RegisterGenerator(register, findCourse(register)).getAmounth;}
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{RegisterGenerator regGenerator =new RegisterGenerator(item, findCourse(item));Register reg = (Register)item.Clone();reg.course = regGenerator.course;//reg.amount = getAmounth(reg);reg.amount = regGenerator.getAmounth;reg.valumeCredit = calculateVolumeCredit(reg);return reg;}).ToArray();}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public class RegisterGenerator { public Register register { get; set; } public Course course { get; set; } public int getAmounth { get { var resultAmounth = 0; switch (this.course.Type) { case Types.Art: { resultAmounth = 3000; if (this.register.student > 15) { resultAmounth += 1000 * (this.register.student - 10); } break; } case Types.Software: { resultAmounth = 30000; if (this.register.student > 10) { resultAmounth += 10000 + 500 * (this.register.student - 5); } resultAmounth += 300 * this.register.student; break; } } return resultAmounth; } } public decimal getValumeCredit { get { decimal volumeCredits = 0; //kazanılan para puan volumeCredits += Math.Max(this.register.student - 15, 0); // extra bonus para puan her 5 yazılım öğrencisi için decimal fiveStudentGroup = this.register.student / 5; if (Types.Software == this.course.Type) volumeCredits += Math.Floor(fiveStudentGroup); return volumeCredits; } } public RegisterGenerator(Register _register, Course _course) { this.register = _register; this.course = _course; } } |
İstenir ise calculateVolumeCredit() methodu aşağıdaki gibi değiştirilebilir: Biz yine bu örnekde ilgili methoda doğrudan erişmeyeceğiz.
public static decimal calculateVolumeCredit(Register register){return new RegisterGenerator(register, findCourse(register)).getValumeCredit;}
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{RegisterGenerator regGenerator =new RegisterGenerator(item, findCourse(item));Register reg = (Register)item.Clone();reg.course = regGenerator.course;reg.amount = regGenerator.getAmounth;/*reg.valumeCredit = calculateVolumeCredit(reg);*/reg.valumeCredit = regGenerator.getValumeCredit;return reg;}).ToArray();}
1 2 3 4 |
public static RegisterGenerator createRegisterGenerator(Register reg, Course course) { return new RegisterGenerator(reg, course); } |
public static Register[] CloneRegister(this Register[] arrayToClone){return arrayToClone.Select(item =>{/*RegisterGenerator regGenerator =new RegisterGenerator(item, findCourse(item));*/RegisterGenerator regGenerator = createRegisterGenerator(item, findCourse(item));Register reg = (Register)item.Clone();reg.course = regGenerator.course;reg.amount = regGenerator.getAmounth;reg.valumeCredit = regGenerator.getValumeCredit;return reg;}).ToArray();}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class ArtRegisterGenerator : RegisterGenerator { public ArtRegisterGenerator(Register _register, Course _course) : base(_register, _course){} public override int getAmounth { get { var resultAmounth = 3000; if (this.register.student > 15) { resultAmounth += 1000 * (this.register.student - 10); } return resultAmounth; } } } public class SoftwareRegisterGenerator : RegisterGenerator { public SoftwareRegisterGenerator(Register _register, Course _course) : base(_register, _course){} public override int getAmounth { get { var resultAmounth = 30000; if (this.register.student > 10) { resultAmounth += 10000 + 500 * (this.register.student - 5); } resultAmounth += 300 * this.register.student; return resultAmounth; } } } |
RegisterGenerator aşağıdaki gibi değiştirilir: Switch Case’e göre hesaplanan getAmounth property’si, artık bir değer döndürmez. Kendisinden türetülen sınıfın tipine göre, farklı bussiness planlara ait sonuçlar ilgili sınıflar tarafından döndürülür.
public class RegisterGenerator{public Register register { get; set; }public Course course { get; set; }public virtual int getAmounth{get{var result Amounth=0;switch (this.course.Type){case Types.Art:{/* resultAmounth = 3000;if (this.register.student > 15){resultAmounth += 1000 * (this.register.student – 10);}break;*/throw new Exception(“Subclass Responsibility”);}case Types.Software:{/* resultAmounth = 30000;if (this.register.student > 10){resultAmounth += 10000 + 500 * (this.register.student – 5);}resultAmounth += 300 * this.register.student;break; */throw new Exception(“Subclass Responsibility”);}}return resultAmounth;}}public virtual decimal getValumeCredit{get{decimal volumeCredits=0;//kazanılan para puanvolumeCredits+=Math.Max(this.register.student-15, 0);//extra bonus para puan her 5 yazılım öğrencisi içindecimal fiveStudentGroup=this.register.student/5;if (Types.Software==this.course.Type) volumeCredits+=Math.Floor(fiveStudentGroup);return volumeCredits;}}public RegisterGenerator(Register_register, Course_course){this.register=_register;this.course=_course;}}
public static RegisterGenerator createRegisterGenerator(Register reg, Course course){//return new RegisterGenerator(reg, course);switch (course.Type){case Types.Art: return new ArtRegisterGenerator(reg, course);case Types.Software: return new SoftwareRegisterGenerator(reg, course);default:throw new Exception($”Unknown type: { course.Type }”);}}
public class RegisterGenerator{public Register register { get; set; }public Course course { get; set; }public virtual int getAmounth{get{throw new Exception(“Subclass Responsibility”);}}public virtual decimal getValumeCredit{get{return Math.Max(this.register.student – 15, 0);/*decimal volumeCredits=0;volumeCredits+=Math.Max(this.register.student-15, 0);decimal fiveStudentGroup=this.register.student/5;if (Types.Software==this.course.Type) volumeCredits+=Math.Floor(fiveStudentGroup);return volumeCredits;*/}}public RegisterGenerator(Register_register, Course_course){this.register=_register;this.course=_course;}}
public class SoftwareRegisterGenerator : RegisterGenerator{public SoftwareRegisterGenerator(Register_register, Course_course) : base(_register, _course){}public override int getAmounth{get{var resultAmounth=30000;if (this.register.student>10){resultAmounth+=10000+500 * (this.register.student-5);}resultAmounth+=300 * this.register.student;return resultAmounth;}}
public override decimal getValumeCredit{get{decimal fiveStudentGroup=this.register.student/5;return base.getValumeCredit + Math.Floor(fiveStudentGroup);}}}
public class ArtRegisterGenerator : RegisterGenerator{public ArtRegisterGenerator(Register_register, Course_course) : base(_register, _course){}public override int getAmounth{get{var resultAmounth=3000;if (this.register.student>15){resultAmounth+=1000* (this.register.student-10);}return resultAmounth;}}}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace Refactoring { public class ArtRegisterGenerator : RegisterGenerator { public ArtRegisterGenerator(Register _register, Course _course) : base(_register, _course){} public override int getAmounth { get { var resultAmounth = 3000; if (this.register.student > 15) { resultAmounth += 1000 * (this.register.student - 10); } return resultAmounth; } } } public class SoftwareRegisterGenerator : RegisterGenerator { public SoftwareRegisterGenerator(Register _register, Course _course) : base(_register, _course){} public override int getAmounth { get { var resultAmounth = 30000; if (this.register.student > 10) { resultAmounth += 10000 + 500 * (this.register.student - 5); } resultAmounth += 300 * this.register.student; return resultAmounth; } } public override decimal getValumeCredit { get { decimal fiveStudentGroup = this.register.student / 5; return base.getValumeCredit + Math.Floor(fiveStudentGroup); } } } public class RegisterGenerator { public Register register { get; set; } public Course course { get; set; } public virtual int getAmounth { get { throw new Exception("Subclass Responsibility"); } } public virtual decimal getValumeCredit { get { return Math.Max(this.register.student - 15, 0); } } public RegisterGenerator(Register _register, Course _course) { this.register = _register; this.course = _course; } } static class Program { public static RegisterGenerator createRegisterGenerator(Register reg, Course course) { switch (course.Type) { case Types.Art: return new ArtRegisterGenerator(reg, course); case Types.Software: return new SoftwareRegisterGenerator(reg, course); default: throw new Exception($"Unknown type: { course.Type }"); } } public static Register[] CloneRegister(this Register[] arrayToClone) { return arrayToClone.Select(item => { RegisterGenerator regGenerator = createRegisterGenerator(item, findCourse(item)); Register reg = (Register)item.Clone(); reg.course = regGenerator.course; reg.amount = regGenerator.getAmounth; reg.valumeCredit = regGenerator.getValumeCredit; return reg; }).ToArray(); } static IDictionary<string, Course> courses; static Invoice invoice; static void Main(string[] args) { Console.WriteLine("Wellcome to Refactoring Example"); courses = new Dictionary<string, Course>(); courses.Add("dpattern", new Course() { Name = "Design Pattern", Type = Types.Software }); courses.Add("hface", new Course() { Name = "Human Face", Type = Types.Art }); courses.Add("redis", new Course() { Name = "Redis", Type = Types.Software }); invoice = new Invoice(); invoice.customerName = "Hasel Team"; invoice.registers = new Register[] { new Register () { courseID = "dpattern", student = 20 }, new Register () { courseID = "hface", student = 15 }, new Register () { courseID = "redis", student = 5 }, }; PassingData passingData = new PassingData(); passingData.customerName = invoice.customerName; passingData.registers = invoice.registers.CloneRegister(); passingData.TotalAmount = getTotalAmount(passingData); passingData.TotalValumeCredits = totalValumeCredits(passingData); CalculateAmounth(passingData, courses); } public static Course findCourse(Register register) { return courses[register.courseID]; } public static int getAmounth(Register register) { return new RegisterGenerator(register, findCourse(register)).getAmounth; } public static decimal calculateVolumeCredit(Register register) { return new RegisterGenerator(register, findCourse(register)).getValumeCredit; } public static decimal totalValumeCredits(PassingData data) { decimal resultValume = 0; foreach (Register reg in data.registers) { resultValume += reg.valumeCredit; } return resultValume; } public static decimal getTotalAmount(PassingData data) { decimal resultTotalAmounth = 0; foreach (Register reg in data.registers) { resultTotalAmounth += reg.amount; } return resultTotalAmounth; } public static void CalculateAmounth(PassingData data, IDictionary<string, Course> courses) { var result = $"{data.customerName} için Fatura Detayı: \n"; foreach (Register reg in data.registers) { result += $"{reg.course.Name}: {tr(reg.amount / 100)} ({reg.student} kişi)\n"; } result += $"Toplam borç { tr(data.TotalAmount / 100) }\n"; result += $"Kazancınız { tr(data.TotalValumeCredits) } \n"; Console.WriteLine(result); Console.ReadLine(); string tr(decimal value) { CultureInfo trFormat = new CultureInfo("tr-TR", false); trFormat.NumberFormat.CurrencySymbol = "TL"; trFormat.NumberFormat.NumberDecimalDigits = 2; return value.ToString("C", trFormat); } } } } |
“İyi bir kodun gerçek testi, ne kadar kolay değiştirilebildiği ile alakalıdır!”Martin Fowler…
Merhaba,
Program sınıftaki getAmounth ve calculateVolumeCredit atıl kodlardır. Bir işlevleri yok. Refactoring sonrası kaldırılabilir.
Bir yazılımcı için çok önemli olduğunu düşündüğüm refactoring konusunda hazırlamış olduğun makale için de çok teşekkürler.
Emeğine sağlık.
Teşekkürler Ali,
Evet bu 2 method atıldır. Atıl olan detay birşey daha var. Onu da bulursan, bu makaleye yutmuşun demektir :)
İyi çalışmalar.
Main de atıl durumdaki string[] args kaldırıyoruz. :-)
Hahaha :) Yani o da atıl aslında. Ama o değil :)
GetTotalAmount ve TotalValumeCredits methodlarını tek bir method da birleştirebiliriz.
public static decimal Total(PassingData data,string fieldName)
{
decimal result = 0;
foreach (Register reg in data.registers)
{
result += Convert.ToDecimal(reg.GetType().GetProperty(fieldName).GetValue(reg, null));
}
return result;
}
Ayrıca CalculateAmounth methodunda ikinci parametre IDictionary courses yi kaldırabiliriz.