Static Cling

Sticking Your Code To Things Unnecessarily

Static Cling is a code smell used to describe the undesirable coupling introduced by accessing static (global) functionality, either as variables or methods. This coupling can make it difficult to test or modify the behavior of software systems.

Consider the following example:

public class CheckoutController
{
public void Checkout(Order order)
{
// verify payment // verify inventory LogHelper.LogOrder(order);
}
} public static class LogHelper
{
public static void LogOrder(Order order)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(@"C:\Users\Steve\OrderLog.txt", true))
{
file.WriteLine("{0} checked out.", order.Id);
}
}
} public class Order
{
public int Id { get; set; }
}

In the above code, any attempt to unit test the Checkout method will be made much more difficult by the static LogOrder method, which has a dependency on the file system and a particular file path.

While it’s certainly possible to write an integration test that will still log to the chosen path, or to refactor this code so that the file path comes from configuration or something similar, it would be far better if the dependency on the file system didn’t exist, since it isn’t important to what Checkout() is trying to do.

To refactor away from Static Cling, replace the static method call with an instance method call on an instance type (frequently implementing an interface), and use the strategy design pattern (also known as dependency injection) to inject the dependency into the class that needs the functionality.

In the case where the static functionality is not code you control, you can access it through an Adapter. This approach is shown below:

public class CheckoutController
{
private readonly IOrderLoggerAdapter _orderLoggerAdapter; public CheckoutController(IOrderLoggerAdapter orderLoggerAdapter)
{
_orderLoggerAdapter = orderLoggerAdapter;
} public CheckoutController()
: this(new FileOrderLoggerAdapter())
{ } public void Checkout(Order order)
{
// verify payment // verify inventory _orderLoggerAdapter.LogOrder(order);
}
} public static class LogHelper
{
public static void LogOrder(Order order)
{
using (System.IO.StreamWriter file =
new System.IO.StreamWriter(@"C:\Users\Steve\OrderLog.txt", true))
{
file.WriteLine("{0} checked out.", order.Id);
}
}
} public interface IOrderLoggerAdapter
{
void LogOrder(Order order);
} public class FileOrderLoggerAdapter : IOrderLoggerAdapter
{
public void LogOrder(Order order)
{
LogHelper.LogOrder(order);
}
} public class Order
{
public int Id { get; set; }
}

In the above code, the OrderController no longer has a direct dependency on the static LogHelper.LogOrder() method.

It now follows the Explicit Dependencies Principle, since its constructor declares the collaborating types it requires to function.

This would allow the code to be modified in the future by simply passing in a different implementation of the IOrderLoggerAdapter,

and would also allow unit tests to test the other behavior in the Checkout() method without the need for certain drives, paths, or files to exist on the test machine.

If the application is using a container to resolve class dependencies, configuring the runtime behavior of how OrderController will get the classes it depends on would be done in the container’s configuration.

If a container is not in use, or if existing client code needs to continue to call the default constructor of OrderController, a technique called poor man’s dependency injection can be used.

With this technique, a default constructor is configured to call through to the constructor that accepts dependencies, with instances configured that provide the original behavior.

In this case, the default constructor passes a new instance of the FileOrderLoggerAdapter, which contains the original behavior of calling LogHelper.LogOrder().

Although Static Cling refers specifically to references to static methods (or properties), the same consequences occur when instance variables are instantiated and immediate called within a method.

Be careful of where in your code you make decisions about a method or class’s collaborators, and remember that New is Glue if you choose to instantiate a type that has dependencies on infrastructure concerns (e.g. file system, database, etc).

相关链接:

antipatterns

Principles

Static需谨慎的更多相关文章

  1. 从Go、Swift出发:语言的选择需谨慎

    本文转自 : http://www.csdn.net/article/2014-12-09/2823025 摘要:无论是开源的Go,还是闭源的Swift,新的语言总是利弊一体.不过可以确定的是,新的语 ...

  2. IOS开发中重写init方法使用需谨慎

    IOS开发中重写init方法使用需谨慎 今天在写一个小软件的时候出现一点问题,这个软件的功能是搜索全国学校,首页就是搜索输入框,在框中输入完要查询的学校所在省份,点击buttom后就会跳转到对应的视图 ...

  3. PHP就业前景好不好一看便知,转行选择需谨慎!

    随着互联网行业迎来新一波的热潮,更多的年轻人选择软件行业发展.由于互联网本身快速发展.不断创新的特点,决定了只有以快开发速度和低成本,才能赢得胜利,才能始终保持网站的领先性和吸引更多的网民. 互联网的 ...

  4. 借root之名,行流氓之实,劝告,root需谨慎

    20160425++++++ 今日再回头看这篇文章,貌似有点偏激了一点,不过xda论坛上有个疑似kingroot开发团队的用户说明了kingroot确实对supersu做了限制,说是supersu在替 ...

  5. Xcode8-beat升级需谨慎

    Xcode8-beat版本在打开xib文件的时候,出现了如下的弹窗 在这里要选择Cancel,选择Choose后xib文件的verson会改变,那么Xcode7就没法打开了(坑队友啦), 更没法运行 ...

  6. 检验appium环境是否正常:使用appium自动给手机安装app(注意:如果已存在该app,再执行会将原来的卸载再重装,需谨慎)

    (注意:如果已存在该app,再执行会将原来的卸载再重装.泪的教训,我的微信被卸载重装了o(╥﹏╥)o,自动安装app这个步骤需谨慎操作) hi,前面几篇已经讲了appium环境的搭建.设备的连接, 那 ...

  7. Git存储用户名和密码(明文需谨慎)

    当你配置好git后,在C:\Documents and Settings\Administrator\ 目录下有一个 .gitconfig 的文件,里面会有你先前配好的name 和email,只需在下 ...

  8. New需谨慎

    New is Glue When you’re working in a strongly typed language like C# or Visual Basic, instantiating ...

  9. 设计数据库字段或者java中使用boolean型时需谨慎

    boolean型变量只有两个值 false和true,我们在设计数据库字段时或者定义java变量时会使用boolean,通常情况下开关类的变量使用无可非议,但请一定要考虑到扩展性. 使用前请仔细考虑一 ...

随机推荐

  1. centos 安装 python36

    centos6 安装 python36 临时方法: https://www.softwarecollections.org/en/scls/rhscl/rh-python36/ 方法二: http:/ ...

  2. ArcGIS AddIn调用ArcMap自带的对话框

    ESRI.ArcGIS.Framework命名空间提供了ArcGIS常用的一些对话框,可以在开发时直接调用这些对话框,而不需要重新去写Form 主要对话框有 1.IColorBrowser/IColo ...

  3. Oracle课程档案,第十天

    用户管理 Authentication: 身份验证 AAA:Authentication: 身份验证 Authorization: 权限管理 Audition: 审计 grant:授权 unset:撤 ...

  4. git fork代码并修改胡提交到自己的git仓库

    最近在参加阿里天池大数据中间件比赛(毫无头绪,打酱油中).看参赛要求,需要将官网的git工程clone下来,在此基础上做修改后提交到自己的仓库中. 由于以前并没有使用过git,所以差了比较多的资料,做 ...

  5. Centos下搭建邮件服务器

    一.协议 SMTP:用于发送邮件 POP3:用于接收邮件,接收后会将服务器上邮件删除 IMAP:用于接收邮件,接收后不会删除服务器邮件 二.几个重要的角色 MUA:可以理解为收取邮件的工具,比如thu ...

  6. Flask主要知识点

    Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后 ...

  7. Oracle 数据库逻辑结构.md

    一.存储关系Oracle 数据库逻辑上是由一个或多个表空间组成的,表空间物理上是由一个或多个数据文件组成的:而在逻辑上表空间又是由一个或多个段组成的.在Oracle 数据库中,通过为每种不同的数据对象 ...

  8. 011-MAC 设置环境变量path的几种方法

    一.概述 首先要知道你使用的Mac OS X是什么样的Shell,使用命令 echo $SHELL 如果输出的是:csh或者是tcsh,那么你用的就是C Shell. 如果输出的是:bash,sh,z ...

  9. Mybatis经常被问到的面试题

    1. #{}和${}的区别是什么? #{}是预编译处理,${}是字符串替换. Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值: ...

  10. IP-v4&IP-v6

    IPv6与IPv4区别: 1:IPv6的地址空间更大.IPv4中规定IP地址长度为32,即有2^32-1个地址: 而IPv6中IP地址的长度为128,即有2^128-1个地址. 2.IPv6的路由表更 ...