气味识别应用_代码气味–第二部分

气味识别应用

在上一篇文章《代码气味–第一部分》中 ,我谈到了膨胀器:它们是代码气味,可以识别为长方法,大型类,原始痴迷,长参数列表和数据块。 在这一篇中,我想深入研究面向对象的滥用者变更阻止者

面向对象的滥用者

当面向对象的原则不完整或应用不正确时,通常会发生这种类型的代码异味。

切换语句

这种情况很容易识别:我们有一个开关箱。 但是,如果您发现一系列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

气味识别应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/336228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

activiti高亮显示图片_如今透明LED显示屏成为广告橱窗,它们之间有什么关联?...

如今我们在街道行走时,我们就会看到各种玻璃橱窗,这是商家想利用橱窗进行不同产品宣传角度。其实无论是海报、灯箱或是实物展示,这些都是商家通过橱窗广告进行来吸引消费者,而现在是5G时代到来使橱窗数字化营销成为了一种新的趋势…

theme editor android,谷歌宣布将于下月停用 Material Theme Editor

使用 Material Theme Editor,开发人员可创建和自定义 Material 主题,包括颜色、形状、版式等等。在 Material Theme Editor 中,可根据不同部分依次选择颜色,并应用于所有组件,还可调整对比度,根据单一颜色自…

【WebRTC---入门篇】(十六)端对端1V1传输基本流程

RTCPeerConnection 媒体协商 setLocalDescription方法 ,收集所有候选者。 setRemoteDescription方法, 放到远端的描述槽中。 协商状态变化

pytorch dataset_【小白学PyTorch】16.TF2读取图片的方法

<>扩展之tensorflow2.0 | 15 TF2实现一个简单的服装分类任务小白学PyTorch | 14 tensorboardX可视化教程小白学PyTorch | 13 EfficientNet详解及PyTorch实现小白学PyTorch | 12 SENet详解及PyTorch实现小白学PyTorch | 11 MobileNet详解及PyTorch实现小白学PyTorch | 10 …

android 硬件对接,Android 对接硬件串口篇

private DispQueueThread DispQueue;private AssistBean AssistData;private SerialControl ComA;Overridepublic void initView() {ComA new SerialControl();//开启串口DispQueue new DispQueueThread();//开启线程实时获取数据DispQueue.start();//线程启动AssistData ge…

【WebRTC---入门篇】(十七)实现1V1音视频实时互动直播系统

STUN/TURN服务器搭建 详细搭建过程 RTCPeerConnection

junit5和junit4_JUnit 5 –架构

junit5和junit4现在我们知道如何设置JUnit 5并使用它编写一些测试 &#xff0c;下面让我们看一下。 在本文中&#xff0c;我们将讨论JUnit 5架构以及采用这种方式的原因。 总览 这篇文章是有关JUnit 5的系列文章的一部分&#xff1a; 建立 基本 建筑 条件 注射 … JUni…

pytorch load state dict_PyTorch 学习笔记(五):Finetune和各层定制学习率

本文截取自《PyTorch 模型训练实用教程》&#xff0c;获取全文pdf请点击&#xff1a;https://github.com/tensor-yu/PyTorch_Tutorial [toc]我们知道一个良好的权值初始化&#xff0c;可以使收敛速度加快&#xff0c;甚至可以获得更好的精度。而在实际应用中&#xff0c;我们通…

华为配备鸿蒙系统的手机,华为P50/新平板双双来袭!全球首发鸿蒙系统:配置都非常强悍...

【12月12日讯】相信大家都知道&#xff0c;华为方面已经正式官宣&#xff0c;将会在12月16日正式推出鸿蒙系统首个手机Bate版本&#xff0c;但也有很多网友们担忧&#xff0c;华为手机在脱离了Android系统以后&#xff0c;鸿蒙OS系统是否真的可以击败Android系统&#xff0c;第…

【WebRTC---入门篇】(十八)WebRTC非音视频数据传输

WebRTC传输非音视频重要API createDataChannel options ordered 在传输非音视频的时候是否是按序到达的。 maxPacketLifeTime/maxRetransmits 最大包存活时间;最大传输次数。两者二选一 negotiated ID 唯一标识 DataChannel事件

ios 静音模式_静音设计模式

ios 静音模式您最近是否遵循Mute-Design-Pattern™编写了大量代码&#xff1f; 例如 try {complex();logic();here(); } catch (Exception ignore) {// Will never happen heheSystem.exit(-1); }Java 8有一个更简单的方法&#xff01; 只需将这个非常有用的工具添加到您的Ut…

datatable使用_使用Streamlit从简单的Python脚本创建交互式WebApp

如果有人告诉您可以使用150-200行代码创建交互式Web应用程序&#xff0c;该怎么办&#xff1f; 有趣的权利。 Streamlit为您提供了使用简单的python脚本和一些streamlit调用来创建漂亮的Web应用程序的相同机会。Streamlit是一个开放源代码框架&#xff0c;用于以最快的方式创建…

和谐 平等_平等还是认同?

和谐 平等将对象存储在集合中时&#xff0c;同一对象永远不能添加两次很重要。 这是集合的核心定义。 在Java中&#xff0c;使用两种方法来确定两个引用的对象是否相同&#xff0c;或者它们都可以存在于同一Set中。 equals&#xff08;&#xff09;和hashCode&#xff08;&…

html监控用户在线与离线,HTML5判断设备在线离线及监听网络状态变化例子

经测试android ipad默认的浏览器支持&#xff0c;用appcan封装的网页也支持html>网络在线与离线$$function(id){return document.getElementById(id);};if(navigator.onLine){$$("status").innerHTML"第一次加载时在线";}else{$$("status").i…

opengl如何画出一个球_OpenGL-Controlling and Monitoring the Pipeline

全球图形学领域教育的领先者、自研引擎的倡导者、底层技术研究领域的技术公开者&#xff0c;东汉书院在致力于使得更多人群具备内核级竞争力的道路上&#xff0c;将带给小伙伴们更多的公开技术教学和视频&#xff0c;感谢一路以来有你的支持。我们正在用实际行动来帮助小伙伴们…

【WebRTC---入门篇】(二十)WebRTC核心之SDP详解

SDK规范 会话层 媒体层 SDP规范相关参考 WebRTC中的SDP

junit5和junit4_JUnit 5 –条件

junit5和junit4最近&#xff0c;我们了解了JUnit的新扩展模型以及它如何使我们能够将自定义行为注入测试引擎。 我向你保证要看情况。 现在就开始吧&#xff01; 条件允许我们在应该执行或不应该执行测试时定义灵活的标准。 它们的正式名称是“ 条件测试执行” 。 总览 本系列…

android 画圆教程,android shap画圆(空心圆、实心圆)

实心圆&#xff1a;android:shape"oval"android:useLevel"false">android:width"1dp"android:color"color/colorWhite" />android:width"10dp"android:height"10dp" />空心圆&#xff1a;android:shape&…

python opencv输出mp4_10分钟学会使用YOLO及Opencv实现目标检测

点击边框调出视频工具条 计算机视觉领域中&#xff0c;目标检测一直是工业应用上比较热门且成熟的应用领域&#xff0c;比如人脸识别、行人检测等&#xff0c;国内的旷视科技、商汤科技等公司在该领域占据行业领先地位。相对于图像分类任务而言&#xff0c;目标检测会更加复杂一…