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. create database link

    如果本地的tnsnames.ora中未建立数据库连接,那么就是用1,否则就是用2 1:create database link geelyin96 connect to geelyin identif ...

  2. ubuntu下安装php扩展

    参考原文地址:http://www.php.cn/php-weizijiaocheng-341528.html 发现在mac上好像不太行,然后按照下面的可以,写下来与大家分享 利用ubuntu的软件包 ...

  3. 【登录异常解决】Ubuntu 输入正确的密码后重新返回到登陆界面

    症状 Ubuntu 输入正确的密码后,黑屏一闪,重新返回到登陆界面. 原因一:主目录下的.Xauthority文件拥有者变成了root,从而以用户登陆的时候无法都取.Xauthority文件.说明:X ...

  4. pycharm import pygame 出现报错:No module named 'pygame'

    首先发现装的Python 有问题原来的Python3.6.4版本安装完成后Scripts文件夹里空白的,什么也没有,从https://www.python.org/downloads/windows/ ...

  5. Luogu4451 [国家集训队]整数的lqp拆分

    题目链接:洛谷 题目大意:求对于所有$n$的拆分$a_i$,使得$\sum_{i=1}^ma_i=n$,$\prod_{i=1}^mf_{a_i}$之和.其中$f_i$为斐波那契数列的第$i$项. 数 ...

  6. Spring MVC原理及配置详解

    Spring MVC概述: Spring MVC是Spring提供的一个强大而灵活的web框架.借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单.这些控制 ...

  7. DjangoMTV模型之视图层views及模板层template

    Django视图层中views的内容 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容(render),也可以是一个重定向( ...

  8. Spark MLlib之使用Breeze操作矩阵向量

    在使用Breeze 库时,需要导入相关包: import breeze.linalg._ import breeze.numerics._ Breeze创建函数 //全0矩阵 DenseMatrix. ...

  9. 大牛推荐的30本经典编程书籍,从Python到前端全系列。

    注:为了方便阅读与收藏,我们也制作了30本书籍完整清单的Markdown.PDF版以及思维导图版,大家可以在实验楼公众号后台回复关键字"书籍推荐"获取. Python 系列(10本 ...

  10. arch----------arch下的一些命令,亲测

    1.taoyanghao 不在 sudoers 文件中.此事将被报告. 这个是使用sudo以后报出的错误提示,sudo确定已经安装了. 解决方案:编辑/etc/sudoers文件.找到这一 行:&qu ...