如此理解面向对象编程

阿酷 发布于 2012/12/17 18:18
阅读 6K+
收藏 116

从Rob Pike 的 Google+上的一个推看到了一篇叫《Understanding Object Oriented Programming》的文章,我先把这篇文章简述一下,然后再说说老牌黑客Rob Pike的评论。

先看这篇教程是怎么来讲述OOP的。它先给了下面这个问题,这个问题需要输出一段关于操作系统的文字:假设Unix很不错,Windows很差。

这个把下面这段代码描述成是Hacker Solution。(这帮人觉得下面这叫黑客?我估计这帮人真是没看过C语言的代码)

public class PrintOS
{
    public static void main(final String[] args)
    {
        String osName = System.getProperty("os.name") ;
        if (osName.equals("SunOS") || osName.equals("Linux"))
        {
            System.out.println("This is a UNIX box and therefore good.") ;
        }
        else if (osName.equals("Windows NT") || osName.equals("Windows 95"))
        {
            System.out.println("This is a Windows box and therefore bad.") ;
        }
        else
        {
            System.out.println("This is not a box.") ;
        }
    }
}

然后开始用面向对象的编程方式一步一步地进化这个代码。

先是以过程化的思路来重构之。

过程化的方案

public class PrintOS
{
    private static String unixBox()
    {
        return "This is a UNIX box and therefore good." ;
    }
    private static String windowsBox()
    {
        return "This is a Windows box and therefore bad." ;
    }
    private static String defaultBox()
    {
        return "This is not a box." ;
    }
    private static String getTheString(final String osName)
    {
        if (osName.equals("SunOS") || osName.equals("Linux"))
        {
            return unixBox() ;
        }
        else if (osName.equals("Windows NT") ||osName.equals("Windows 95"))
        {
            return windowsBox() ;
        }
        else
        {
            return defaultBox() ;
        }
    }
    public static void main(final String[] args)
    {
        System.out.println(getTheString(System.getProperty("os.name"))) ;
    }
}
然后是一个幼稚的面向对象的思路。

幼稚的面向对象编程

PrintOS.java

public class PrintOS
{
    public static void main(final String[] args)
    {
        System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
    }
}
OSDiscriminator.java

public class OSDiscriminator // Factory Pattern
{
    private static BoxSpecifier theBoxSpecifier = null ;
    public static BoxSpecifier getBoxSpecifier()
    {
        if (theBoxSpecifier == null)
        {
            String osName = System.getProperty("os.name") ;
            if (osName.equals("SunOS") || osName.equals("Linux"))
            {
                theBoxSpecifier = new UNIXBox() ;
            }
            else if (osName.equals("Windows NT") || osName.equals("Windows 95"))
            {
                theBoxSpecifier = new WindowsBox() ;
            }
            else
            {
                theBoxSpecifier = new DefaultBox () ;
            }
        }
        return theBoxSpecifier ;
    }
}
BoxSpecifier.java

public interface BoxSpecifier
{
    String getStatement() ;
}
DefaultBox.java

public class DefaultBox implements BoxSpecifier
{
    public String getStatement()
    {
        return "This is not a box." ;
    }
}
UNIXBox.java

public class UNIXBox implements BoxSpecifier
{
    public String getStatement()
    {
        return "This is a UNIX box and therefore good." ;
    }
}
WindowsBox.java

public class WindowsBox implements BoxSpecifier
{
    public String getStatement()
    {
        return "This is a Windows box and therefore bad." ;
    }
}

他们觉得上面这段代码没有消除if语句,他们说这叫代码的“logic bottleneck”(逻辑瓶颈),因为如果你要增加一个操作系统的判断的话,你不但要加个类,还要改那段if-else的语句。

所以,他们整出一个叫Sophisticated的面向对象的解决方案。

OO大师的方案

注意其中的Design Pattern

PrintOS.java

public class PrintOS
{
    public static void main(final String[] args)
    {
        System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
    }
}
OSDiscriminator.java

public class OSDiscriminator // Factory Pattern
{
    private static java.util.HashMap storage = new java.util.HashMap() ;
 
    public static BoxSpecifier getBoxSpecifier()
    {
        BoxSpecifier value = (BoxSpecifier)storage.get(System.getProperty("os.name")) ;
        if (value == null)
            return DefaultBox.value ;
        return value ;
    }
    public static void register(final String key, final BoxSpecifier value)
    {
        storage.put(key, value) ; // Should guard against null keys, actually.
    }
    static
    {
        WindowsBox.register() ;
        UNIXBox.register() ;
        MacBox.register() ;
    }
}
BoxSpecifier.java

public interface BoxSpecifier
{
    String getStatement() ;
}
DefaultBox.java

public class DefaultBox implements BoxSpecifier // Singleton Pattern
{
    public static final DefaultBox value = new DefaultBox () ;
    private DefaultBox() { }
    public String getStatement()
    {
        return "This is not a box." ;
    }
}
UNIXBox.java

public class UNIXBox implements BoxSpecifier // Singleton Pattern
{
    public static final UNIXBox value = new UNIXBox() ;
    private UNIXBox() { }
    public  String getStatement()
    {
        return "This is a UNIX box and therefore good." ;
    }
    public static final void register()
    {
        OSDiscriminator.register("SunOS", value) ;
        OSDiscriminator.register("Linux", value) ;
    }
}
WindowsBox.java

public class WindowsBox implements BoxSpecifier  // Singleton Pattern
{
    public  static final WindowsBox value = new WindowsBox() ;
    private WindowsBox() { }
    public String getStatement()
    {
        return "This is a Windows box and therefore bad." ;
    }
    public static final void register()
    {
        OSDiscriminator.register("Windows NT", value) ;
        OSDiscriminator.register("Windows 95", value) ;
    }
}
MacBox.java

public class MacBox implements BoxSpecifier // Singleton Pattern
{
    public static final MacBox value = new MacBox() ;
    private MacBox() { }
    public  String getStatement()
    {
        return "This is a Macintosh box and therefore far superior." ;
    }
    public static final void register()
    {
        OSDiscriminator.register("Mac OS", value) ;
    }
}

作者还非常的意地说,他加了一个“Mac OS”的东西。老实说,当我看到最后这段OO大师搞出来的代码,我快要吐了。我瞬间想到了两件事:一个是以前酷壳上的《面向对象是个骗局》和 《各种流行的编程方式》中说的“设计模式驱动编程”,另一个我想到了那些被敏捷洗过脑的程序员和咨询师,也是这种德行。

于是我去看了一下第一作者Joseph Bergin的主页,这个Ph.D是果然刚刚完成了一本关于敏捷和模式的书。

Rob Pike的评论

(Rob Pike是当年在Bell lab里和Ken一起搞Unix的主儿,后来和Ken开发了UTF-8,现在还和Ken一起搞Go语言。注:不要以为Ken和Dennis是基友,其实他们才是真正的老基友!)

Rob Pike在他的Google+的这贴里评论到这篇文章——

他并不确认这篇文章是不是搞笑?但是他觉得这些个写这篇文章是很认真的。他说他要评论这篇文章是因为他们是一名Hacker,至少这个词出现在这篇文章的术语中。

他说,这个程序根本就不需要什么Object,只需要一张小小的配置表格,里面配置了对应的操作系统和你想输出的文本。这不就完了。这么简单的设 计,非常容易地扩展,他们那个所谓的Hack Solution完全就是笨拙的代码。后面那些所谓的代码进化相当疯狂和愚蠢的,这个完全误导了对编程的认知。

然后,他还说,他觉得这些OO的狂热份子非常害怕数据,他们喜欢用多层的类的关系来完成一个本来只需要检索三行数据表的工作。他说他曾经听说有人在他的工作种用各种OO的东西来替换While循环。(我听说中国Thoughtworks那帮搞敏捷的人的确喜欢用Object来替换所有的if-else语句,他们甚至还喜欢把函数的行数限制在10行以内)

他还给了一个链接http://prog21.dadgum.com/156.html,你可以读一读。最后他说,OOP的本质就是——对数据和与之关联的行为进行编程。便就算是这样也不完全对,因为:

Sometimes data is just data and functions are just functions.

我的理解

我觉得,这篇文章的例子举得太差了,差得感觉就像是OO的高级黑。面向对象编程注重的是:1)数据和其行为的打包封装,2)程序的接口和实现的解耦。你那怕,举一个多个开关和多个电器的例子,不然就像STL中,一个排序算法对多个不同容器的例子,都比这个例子要好得多得多。老实说,Java SDK里太多这样的东西了。

我以前给一些公司讲一些设计模式的培训课,我一再提到,那23个经典的设计模式和OO半毛钱关系没有,只不过人家用OO来实现罢了。设计模式就三个准则:1)中意于组合而不是继承,2)依赖于接口而不是实现,3)高内聚,低耦合。你看,这完全就是Unix的设计准则

文章来源: 酷壳 - CoolShell

加载中
1
nardoo
nardoo

禅师:如果把JAVA比作浓妆艳抹的贵妇人,那么C++就是风情万种的美娇娘。

学生:那么C语言呢?

禅师:相比之下,C语言才是纯情无瑕的小清新。

学生:那么汇编语言呢?

禅师:天真可爱的小萝莉。

学生:那么机器代码呢?

禅师:你有恋童癖吗?


lovinglily
lovinglily
看来我追捧贵妇人,嘿嘿~~~
我土鳖
怪不得我是萝莉控,原来汇编用得太多了= =
七液
七液
我喜欢这禅师。C语言是女高中生
0
七液
七液

你忘了说Rob还是奥运会 射箭银牌得主

程序员界中的文武双全还不是胖子的大牛

对了还没有大胡子


另外这篇文章写的很烂,一个结构数组就可以搞定所有问题,为什么又OO又Imp的,实在让人看着心烦有一种很恶心的感觉。

OO本来就没有太大的必要存在,现在有许多C++大牛偏向于高内聚低耦合,类不要继承尽量采用组合的模式来开发。这就和OO的理论不符了,不管是OOP还是OP还是SOA,最关键的本质都是面向数据编程,OO的数据封装就好像是封装了N种数据但是他们分布在N个表里面,你需要跳来跳去,每次修改动一发牵全身,有人说这是你封装技术不好。要重构的。那我就想问了重构就不用修改了?你永远不知道明天会给你提出什么变态需求。你也猜不出明天这个架构是否能满足你未来的需求。OO我不反对,但是我更希望他是一种代码封装方式,然后操作统一的数据格式。至少目前来看许多所谓的不合格的C++高手(不是学院派)他们用C++的理由似乎只有代码封装(C语言没有using没有类所以代码模块化封装很麻烦),我不赞成把数据用OO的思想去封装。

不过这也就是为什么OO的支持者会这样写代码的原因了。因为明天是不可预料的,那最好的办法就是把所有操作接口和数据都封装成class,这样他就有了“无限”扩展能力。看上去完美无缺了。这才是OO可以欺骗大家这么久的原因。因为绝大部分人都贪大求全,也不考虑自己用得到用不到。选最强大的准没错。

Hansoul
Hansoul
回复 @七液 : 可以继承。因为没有编译器的辅助,自然很多东西要自己写自己控制,有些东西可以抽象出来有些则不行。典型的构造和析构函数得自己动手了。
七液
七液
回复 @北京瀚索 : 你确定是是面向对象?而不是面向数据?包装称struct Object是成不了面向对象的。C语言实现面向对象很麻烦,C语言没有虚函数,没有继承,当然你要实现也行只是很麻烦。而且也没必要。面向数据开发才是最根本的。OO神马只是浮云。
七液
七液
回复 @北京瀚索 : using对于包管理的确是好用多了否则C语言就要不停的写XXX_YYY_ZZZ_create这样函数名很麻烦,比较羡慕using,当然了用#define也可以搞定。只是觉得有点怪怪的。个人的一个牢骚而已。using和OO没关系吧?
七液
七液
回复 @傅小黑 : 抢沙发。然后在修改^_^
Hansoul
Hansoul
"C语言没有using没有类所以代码模块化封装很麻烦" 不同意这句。C语言也可以玩面向对象也可以玩模块化,而且还很爽。我们已经把C语言的面向对象(模块化)玩到了分布式模块上了。
下一页
0
静虚
静虚
晕简单问题复杂化。一个枚举搞定。
0
宏哥
宏哥

引用来自“七液”的答案

你忘了说Rob还是奥运会 射箭银牌得主

程序员界中的文武双全还不是胖子的大牛

对了还没有大胡子


另外这篇文章写的很烂,一个结构数组就可以搞定所有问题,为什么又OO又Imp的,实在让人看着心烦有一种很恶心的感觉。

OO本来就没有太大的必要存在,现在有许多C++大牛偏向于高内聚低耦合,类不要继承尽量采用组合的模式来开发。这就和OO的理论不符了,不管是OOP还是OP还是SOA,最关键的本质都是面向数据编程,OO的数据封装就好像是封装了N种数据但是他们分布在N个表里面,你需要跳来跳去,每次修改动一发牵全身,有人说这是你封装技术不好。要重构的。那我就想问了重构就不用修改了?你永远不知道明天会给你提出什么变态需求。你也猜不出明天这个架构是否能满足你未来的需求。OO我不反对,但是我更希望他是一种代码封装方式,然后操作统一的数据格式。至少目前来看许多所谓的不合格的C++高手(不是学院派)他们用C++的理由似乎只有代码封装(C语言没有using没有类所以代码模块化封装很麻烦),我不赞成把数据用OO的思想去封装。

不过这也就是为什么OO的支持者会这样写代码的原因了。因为明天是不可预料的,那最好的办法就是把所有操作接口和数据都封装成class,这样他就有了“无限”扩展能力。看上去完美无缺了。这才是OO可以欺骗大家这么久的原因。因为绝大部分人都贪大求全,也不考虑自己用得到用不到。选最强大的准没错。

class 可以用

OO 绝对是shit

七液
七液
赞成+95588
0
宏哥
宏哥

看似荒诞的用法

实际上, 所有的OO都是这样用的.

OO并没有别的用法.

人们应该 也 可以用类, 但是, 不应该用 "面向对象".

放牛娃1988
放牛娃1988
现在最流行的编程语言,C++/C/JAVA/PHP/PYTHON/OBC/JS,除了祖师爷C,其他的全部都是OOP,就一帮中国NB程序员天天批评OOP,可是一门非OOP语言都设计不出来(别说设计了,理解一门语言都够呛),真是牛逼到家了。
七液
七液
class可以用。OO真的不是什么好东西,慎用或者说不要为了OO而OO。宏哥这个还是看的很明白的
七液
七液
OO靠喷OP,然后又画了一个超大的饼(告诉别人世间万物都是OO都是可以抽象的,OP是过时的。OO可以描述万物)于是大家就认为OO很强大。加上贪大求全的心里就用上了C++,大部分OO又可耻的兼容OP,弄得许多人写的是OP的代码却人为是OO。
0
灵剑子
灵剑子

从部署层面的角度来说,

真正的OO解决方案是这样的,在lib中,扔进去一个xxoo_linux.jar,就可以支持linux了,扔进一个xxoo_macos.jar就可以支持macOs了。

如果达不到这个条件,就没有充分的发挥oo的力量。或者这不是OO的最佳应用场合。


0
南湖船老大
南湖船老大
不做评论,让LZ一个人玩吧
0
吉迪恩
吉迪恩
有些场合没必要一定要用面向对象,而是应该把数据看成是水, 经过打打小小的管道汇集分散。 管道和管道有接口,有阀门,还有过滤器,这样就够了。
0
鞋底两万里
鞋底两万里

oo是为了让代码看起来简单,易维护扩展

还是那句话啊,不要为了oo而oo,搞的那么复杂

0
小耶果
小耶果
OO更像是简化和更优雅调用库而生.对于大多数人来说[System].[IO].[Writeln]比起SystemIOWriteln更容易检索到并有利于IDE的识别并提示.
返回顶部
顶部