免费的网站制作平台,商务贸易网站建设,哈尔滨网络公司有哪些,淘宝客网站怎么批量采集淘宝商品方维采集淘宝数据思路在上一篇文章《代码气味–第一部分》中 #xff0c;我谈到了膨胀器#xff1a;它们是代码气味#xff0c;可以识别为长方法#xff0c;大型类#xff0c;基元痴迷#xff0c;长参数列表和数据块。 在这一篇中#xff0c;我想深入研究面向对象的滥用者和变更阻止者 。 面… 在上一篇文章《代码气味–第一部分》中 我谈到了膨胀器它们是代码气味可以识别为长方法大型类基元痴迷长参数列表和数据块。 在这一篇中我想深入研究面向对象的滥用者和变更阻止者 。 面向对象的滥用者 当面向对象的原则不完整或应用不正确时通常会发生这种类型的代码异味。 切换语句 这种情况很容易识别我们有一个开关箱。 但是如果找到一系列ifs您也应该考虑一下它的气味。 这是变相的开关盒。 为什么switch语句不好 因为添加新条件后您必须查找每次出现这种情况的情况。 因此在与David交谈时他问我如果将开关封装到一个方法中会发生什么情况呢 这确实是一个好问题……如果您的开关盒仅用于“照顾”一种行为仅此而已那就可以了。 记住识别出代码的气味并不意味着您必须经常使用它这是一个权衡。 如果您发现您的switch语句已复制并且每个复制都有不同的行为那么您不能简单地将switch语句隔离在一个方法中。 您需要找到一个合适的“家”才能进入。根据经验当您处于这种情况时应该考虑多态性。 我们可以在此处应用两种重构技术 用子类替换类型代码此技术包括为每种开关情况创建子类并将相应的行为应用于这些子类。 用策略替换类型代码与上述类似在这种情况下您应该使用以下模式之一 状态或策略 。 那么什么时候使用其中一个 如果类型代码没有改变类的行为则可以使用子类技术。 将每个行为分为适当的子类将强制执行“ 单一职责原则”并通常使代码更具可读性。 如果需要添加其他情况您只需在代码中添加一个新类而无需修改任何其他代码。 因此您可以应用“ 打开/关闭原则” 。 当类型代码影响类的行为时应使用策略方法。 如果要更改类字段和许多其他操作的状态则应使用State Pattern 。 如果它仅影响您选择班级行为的方式则“ 策略模式”是一个更好的选择。 嗯...有点混乱不是吗 因此让我们尝试一个例子。 您有一个枚举EmployeeType public enum EmployeeType
{
Worker,
Supervisor,
Manager
} 和一个班级的雇员 public class Employee
{
private float salary;
private float bonusPercentage;
private EmployeeType employeeType;
public Employee(float salary, float bonusPercentage, EmployeeType employeeType)
{
this.salary salary;
this.bonusPercentage bonusPercentage;
this.employeeType employeeType;
}
public float CalculateSalary()
{
switch (employeeType)
{
case EmployeeType.Worker:
return salary;
case EmployeeType.Supervisor:
return salary (bonusPercentage * 0.5F);
case EmployeeType.Manager:
return salary (bonusPercentage * 0.7F);
}return 0.0F;
}
} 一切都很好。 但是如果您需要计算年度奖金怎么办 您将添加另一个这样的方法 public float CalculateYearBonus()
{
switch (employeeType)
{
case EmployeeType.Worker:
return 0;
case EmployeeType.Supervisor:
return salary salary * 0.7F;
case EmployeeType.Manager:
return salary salary * 1.0F;
}return 0.0F;} 看到重复的开关吗 因此让我们首先尝试子类方法这是超类 abstract public class Employee
{ protected float salary;
protected float bonusPercentage;
public EmployeeFinal(float salary, float bonusPercentage)
{
this.salary salary;
this.bonusPercentage bonusPercentage;
}
abstract public float CalculateSalary();virtual public float CalculateYearBonus()
{
return 0.0F;
}} 这里有子类 public class Worker: Employee
{
twopublic Worker(float salary, float bonusPercentage)
: base(salary, bonusPercentage)
{}
override public float CalculateSalary()
{
return salary;
}}public class Supervisor : Employee
{
public Supervisor(float salary, float bonusPercentage)
: base(salary, bonusPercentage)
{}
override public float CalculateSalary()
{
return salary (bonusPercentage * 0.5F);
}
public override float CalculateYearBonus()
{
return salary salary * 0.7F;
}} 使用策略方法我们将创建一个用于计算报酬的接口 public interface IRetributionCalculator
{
float CalculateSalary(float salary);
float CalculateYearBonus(float salary);
} 有了适当的接口我们现在可以将符合该协议的任何类别传递给员工并计算正确的薪水/奖金。 public class Employee
{
private float salary;
private IRetributionCalculator retributionCalculator;
public Employee(float salary, IRetributionCalculator retributionCalculator)
{this.salary salary;
this.retributionCalculator retributionCalculator;
}
public float CalculateSalary()
{
return retributionCalculator.CalculateSalary(salary);
}
public float CalculateYearBonus()
{
return retributionCalculator.CalculateYearBonus(salary);
}
} 临时场 当我们正在计算一些需要多个输入变量的大型算法时就会发生这种情况。 通常在类中创建这些字段没有任何价值因为它们仅用于特定的计算。 这也很危险因为您必须确保在开始下一次计算之前重新初始化它们。 这里最好的重构技术是将Replace Method与Method Object一起使用这会将方法提取到单独的类中。 然后您可以将方法拆分为同一类中的多个方法。 拒绝遗赠 这段代码的气味很难检测因为当子类没有使用其父类的所有行为时就会发生这种情况。 因此好像子类“拒绝”了其父类的某些行为“ bequest”。 在这种情况下如果继续使用继承没有意义那么最好的重构技术就是更改为委派 我们可以通过在子类中创建父类类型的字段来摆脱继承。 这样每次您需要父类中的方法时就将它们委托给这个新对象。 如果继承是正确的事情则从子类中移走所有不必要的字段和方法。 从子类和父类中提取所有方法和字段并将它们放在新类中。 使这个新类成为超类子类和父类应该从该超类继承。 此技术称为提取超类 。 具有不同接口的替代类 嗯这种情况让我想到了同一团队成员之间的“缺乏沟通”因为当我们有两个类做相同的事情但其方法的名称不同时就会发生这种情况。 从重命名方法或移动方法开始 因此您可以使两个类都实现相同的接口。 在某些情况下两个类中仅部分行为重复。 如果是这样请尝试提取超类并使原始类成为子类。 变革预防者 好家伙 这种代码的味道是您真正要避免的。 这些就是在一个地方进行更改时基本上也必须遍历整个代码库在其他地方进行更改的过程。 因此这是我们所有人都想避免的噩梦 发散变化 当您发现自己出于多种不同原因而更改同一班级时就是这种情况。 这意味着您违反了“ 单一责任原则” 与关注点分离有关。 此处应用的重构技术是“ 提取类”因为您要将不同的行为提取到不同的类中。 弹枪手术 这意味着当您在一个班级中进行少量更改时您必须同时更改几个班级。 尽管它看起来与Divergent Change的气味相同但实际上它们是相反的 Divergent Change是对一个类进行多次更改的时间。 gun弹枪外科手术是指同时对多个类别进行一次更改时。 这里要应用的重构技术是Move Method和/或Move Field 。 这将允许您将重复的方法或字段移到一个通用类。 如果该类不存在请创建一个新类。 在原始类几乎保持为空的情况下也许您应该考虑一下该类是否是冗余的如果是请使用Inline Class消除它将其余的方法/字段移至创建的新类之一。 这完全取决于原始类是否不再承担任何责任。 并行继承层次结构 在这种情况下您发现自己为类B创建了一个新的子类因为您向类A添加了一个子类。在这里您可以首先使一个层次结构引用另一个层次结构的实例。 在第一步之后您可以使用“ 移动方法”和“ 移动字段”删除引用类中的层次结构。 您也可以在此处应用“ 访客”模式 。 结论 对于面向对象的滥用者和变更阻止者 我认为如果您知道如何将良好的设计应用于代码则可以更轻松地避免使用它们。 而这伴随着很多实践。 今天我讨论了一些重构技术但还有很多。 您可以在Refactoring.com中找到有关所有内容的良好参考。 就像我在本系列的第一部分中所说的那样代码的气味不能总是被消除。 研究每种情况并做出决定请记住这始终是一个权衡。 翻译自: https://www.javacodegeeks.com/2016/05/code-smells-part-ii.html