Programming Is Boring Art

栏目: IT技术 · 发布时间: 3年前

内容简介:Wow, there is a lot to unpack here. Boring? Art?I said it’s boring because you keep writing code that looks like blocks and blocks of text having the same shape. Assignments, conditionals, loops, functions, etc. I can pull out the best, the most intell

Wow, there is a lot to unpack here. Boring? Art?

I said it’s boring because you keep writing code that looks like blocks and blocks of text having the same shape. Assignments, conditionals, loops, functions, etc. I can pull out the best, the most intelligent code on this planet and put that side by side with another dumb piece of code that someone just wrote for fun. And they look the same from afar.

How boring is it to produce something of the same shape over and over again. And yet, believe it or not, I’ve been doing that for almost 30 years… both professionally and as a hobby. So there is something interesting that keeps dragging me into doing this.

Call it engineering, or whatever you want, but I would consider programming an art. The practice of writing good code requires not just discipline but a different way of logical reasoning that leads to a different world of beauty.

Let me explain.

Everyone knows how to write simple programs that look like linear, step-by-step recipes. That is not what I’m talking about. Practical problems require something more complex. To solve real-world problems, software engineers have to think differently. I shall repeat - think differently.

The secret sauce is abstraction, or so-called generalization. Real good engineers pull themselves out of the practical terms and think deeper with the more general terms.

An example: website health checks

Let’s say I want to write a program that makes a request to a web site and alerts someone if the website is down. The function can simply be written as follows in the Julia language :

function check(url)
    response = HTTP.get(url)
    if response.status != 200 then
        email("someone@somewhere.com", "something is wrong")
    end
end

There is nothing wrong with this function. Really. It does exactly what it should do with the least amount of code. In fact, this is probably what I want to deploy in production as well.

But, it is inflexible.

It is inflexible because it does one thing, and it’s only doing that one thing and nothing else. In other words, it is too rigid. You may wonder, what else do you want, dude?

Just think about that for a little bit. It is not hard to see the following potential problems:

  • The HTTP request only uses the GET method without any query parameter. What if I want to use the POST method? What if I need to pass some kind of id as part of the query parameters in the URL?

  • The code checks the HTTP status against 200 to see if it was successful. What if I want to handle the other 2xx response codes as well?

  • The logic for alerting somenoe is hard-coded to send an email. What if I want to send the alert to a chat platform such as Slack or Zulip?

Generalizing the solution

A skillful programmer looks at the problem and says, hey, why don't we build an abstraction layer to make it more flexible? Let’s walk through the refactoring process below.

What we will do is to maintain the structure/logic of the code and abstract away the rest of the code as more general functions:

function check(url)
    response = fetch(url)
    if !is_successful(response) then
        alert(site, response)
    end
end

The fetch function hits the URL and returns some kind of response object. The is_successful function analyzes the response object and decides if it was successful or not. Finally, the alert function notifies someone about the problem.

These functions are pure abstractions because there is no need to know how they do their job. It’s the same thing as great employees. You don’t need to tell them how to get the job done. They just know and get the job done.

Coming back to the example, the alert function may send an email, log an event to a database, page the support person, or ask Siri to wake you up! You know what I mean. The point is that the check function contains the same logic no matter what these little functions do!

But, hey, each of these little functions still does one thing, right?

Great question. Indeed, that’s exactly what it does at the moment. Let's do a little more. Primarily, we can give the function some context. The first step is to recognize that we need an abstract concept "Fetcher", or something that knows how to fetch data. Logically, such concept can be coded as an abstract type:

abstract type AbstractFetcher end

Then, fetching something via HTTP and checking the result is just one of the many possible implementations:

struct HTTPFetcher <: AbstractFetcher end

fetch(::HTTPFetcher, url) = HTTP.get(url)

function is_successful(::HTTPFetcher, response) 
    return response.status === 200
end

Likewise, we can set up the "notifier" abstract concept the same way.

"""
A notifier knows how to notify someone.
"""
abstract type AbstractNotifier end

"""
Send a notification alert message.
"""
function alert end

Note that the alert function has no body. The purpose of having an empty function is to define what the interface looks like and attach a doc string for documentation purpose.

Just for fun, we will implement both an EmailNotifier and SlackNotifier here:

struct EmailNotifier <: AbstractNotifier 
    recipient::String
end

function alert(::EmailNotifier, subject, message)
    SMTP.send(notifier.recipient, subject, messgae)
end

struct SlackNotifier <: AbstractNotifier 
    channel::String
end

function alert(notifier::SlackNotifier, subject, message)
    Slack.send(notifier.channel, subject, message)
end

>>> Note: SMTP and Slack are "made-up" packages for illustration purpose only. They do not exist.

Finally, the check function just need to accept a fetcher and a notifier object.

function check(url, fetcher::AbstractFetcher, notifier::AbstractNotifier)
    response = fetch(fetcher, site)
    if !is_successful(fetcher, response) then
        alert(notifier, "$site is down", "Error: $response")
    end
end

Just having these few abstractions opens up a lot of possibilities. Why? Let’s suppose that you have 3 different kinds of fetchers and 3 kinds of notifiers. Now, you can easily compose 3 x 3 = 9 different kinds of health checks.

Please beware that abstraction is a double-edged sword.

Too much abstraction can hurt. In the above example, there is only a single level of abstraction. When one looks at the code, it is still pretty easy to understand and comprehend. However, if there are multiple layers of abstraction, then it becomes very difficult to reason about the code. Look no further than Fizz Buzz Enterprise Edition for an extreme illustration of over-engineering.

As you can see, it is quite simple to uplift your code to a new level. The use of abstract type is a quick and easy way to build these absractions. Note that there are other paradigms - for example, traits. I will save that for a future post. If you cannot wait, head over to the BinaryTraits.jl project repo for a quick peek of what's brewing at the moment.

Programming can be fun. It does not have to be boring. At this point, I guess I will retract my initial claim. While my code may look boring from afar, when I look closer, I see a beautiful thing.

Enjoy your programming life. It's a fun art!


以上所述就是小编给大家介绍的《Programming Is Boring Art》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大数据之路

大数据之路

阿里巴巴数据技术及产品部 / 电子工业出版社 / 2017-7-1 / CNY 79.00

在阿里巴巴集团内,数据人员面临的现实情况是:集团数据存储已经达到EB级别,部分单张表每天的数据记录数高达几千亿条;在2016年“双11购物狂欢节”的24小时中,支付金额达到了1207亿元人民币,支付峰值高达12万笔/秒,下单峰值达17.5万笔/秒,媒体直播大屏处理的总数据量高达百亿级别且所有数据都需要做到实时、准确地对外披露……巨大的信息量给数据采集、存储和计算都带来了极大的挑战。 《大数据......一起来看看 《大数据之路》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具