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. ora-01017 和oracle database link

    DB link ,创建完了后总是报密码错误.我感到奇怪.明明密码是对的.怎么可能错误呢. SQL> create public database link kk40 connect to khf ...

  2. bash 脚本。find 命令,xargs

    rm 排除指定文件或文件夹 rm -r !(.git) find 命令两个用法 find <指定目录> <指定条件> <指定动作> $ find . -name ' ...

  3. arch 相关软件及脚本

    安装 arch 脚本 sudo pacman -S arch-install-scripts 安装 ssh  并开启服务 sudo pacman -S open-ssh vim /etc/ssh/ss ...

  4. phjp(ajax)作用知识点

    1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如 ...

  5. Intellij IDEA 生成返回值对象快捷键

    在编写一行JAVA语句时,有返回值的方法已经决定了返回对象的类型和泛型类型,我们只需要给这个对象起个名字就行. 如果使用快捷键生成这个返回值,我们就可以减少不必要的打字和思考,专注于过程的实现. 步骤 ...

  6. Excel GET.DOCUMENT说明

    GET.DOCUMENT(type_num, name_text) Type_num    指明信息类型的数.下表列出 type_num 的可能值与对应结果. Type_num        返回 1 ...

  7. Python----Windous下安装python

    一.  python 安装 1. 下载安装包 https://www.python.org/ftp/python/2.7.14/python-2.7.14.amd64.msi # 2.7安装包 htt ...

  8. vlan之间的通信-单臂路由与三层交换之间的互通

    注:本试验基于单臂路由通信,三层交换通信,请完成以上两个实验,并保证能够通信 熟练掌握单臂路由的配置 熟练掌握三层交换的配置 三层交换与单臂路由的互通 实验原理 三层交换机在原有二层交换机的基础之上增 ...

  9. 20175320 2018-2019-2 《Java程序设计》第3周学习总结

    20175320 2018-2019-2 <Java程序设计>第3周学习总结 教材学习内容总结 本周学习了教材的第四章的内容.在这章中介绍了面向对象编程的概念以及Java编程中的类与对象, ...

  10. ES6 对象

    对象字面量 属性的简洁表示法 ES6允许对象的属性直接写变量,这时候属性名是变量名,属性值是变量值. const age = 12; const name = "Amy"; con ...