`
Ivan_Pig
  • 浏览: 387133 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

变化的平衡点----BrainStorm

阅读更多
PS:如下内容纯属个人想法,有问题,请轻拍!

    早些时候在坛子里看到了这样一个帖子(具体帖子链接找不到了)。内容是这样的。有一个需求:要用*打印出三行的一个三角形。具体怎么实现。相信看到这个题目,有语言基础的人,第一反应就是两个循环,搞定。作者给出的答案如下:
System.out.println("  *");
System.out.println(" ***");
System.out.println("*****");

    第一眼看了可能很好笑,但看了作者的解释的确如此。用户永远是bt的,需求永远是变化的。
   
    而最近在跟chjavach的设计模式,个人感觉桥接模式写得最好,我看了以后思路很清晰,其他模式看完没有那么清晰的感觉。而简单工厂里的这一幅图则是醍醐灌顶啊。让我终于知道了Factory的存在理由了。这可能和个人的开发经历有关,一般都是一个人包揽前后台,而不是分工合作的原因吧。



    设计模式说到底是用来封装变化的,就对上面的题目来看,如果需求没有变化,那么上面的实现是完全可以的。而当需求变化的时候再去重构吧---是重构,不是简单的修改。好像是Bob的《敏捷软件开发:原则、模式与实践》这本书里看到的吧!不要让子弹击中你两次。也就是说第一次以最快实现方式实现需求,当需求更改时,进行重构。

    前段时间看了不少书,数据结构,算法,python,c,c++,gog还有杂七杂八的书籍,当然都没怎么深究了。然后某天晚上睡觉时就想了不少问题!需求是不断变化的。那什么时候使用设计模式?怎么就知道要使用什么样的设计模式了?如果像敏捷开发所说的,重构出模式,什么时候重构?有多大风险?是否不同的语言对变化的解决方案不同呢?如果相同,各位大师发明这么多语言干嘛?如果不同,怎么个不同法呢?

    相信看过HeadFirst设计模式的人对第一章应该印象都比较深刻吧!这就是一个由需求变更而导致重构进而演化出了策略模式的例子。

    这里引用一下他的例子,不过从更原始的模型开始。一开始只有一只鸭子。它能quake,swing,display。如下图所示:



代码如下:
public class Duck {
    public String quack() {
        return "GaGa";
    }

    public String swing() {
        return "Swing";
    }

    public String display() {
        return "Duck Display";
    }
}


测试一下:
public class DuckTest {

    private Duck duck ;
    @Before
    public void setUp() throws Exception {
        duck = new Duck();
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void testQuack() throws Exception {
        assertEquals("GaGa",duck.quack());
    }

    @Test
    public void testSwing() throws Exception {
        assertEquals("Swing",duck.swing());
    }

    @Test
    public void testDisplay() throws Exception {
        assertEquals("Duck Display",duck.display());
    }
}


    这里的Duck就是server端的代码,测试类则相当于client端的代码。

    这段代码肯定是没有问题的,太简单了。如果所有的项目都这么简单,大家都笑了。

    接着出现了第二只鸭子,它是只红头鸭子,它也能quack,swing和display.不同的地方是红头鸭子的display显示的是"RedDuck Display",那么这里能想到的就是继承了,让RedDuck继承Duck,继而覆写Duck的display方法就可以了。



    然后又是,GreenDuck啦之类的,类图变成这样。



    以后如果要添加鸭子,只要添加相应的鸭子类,继承Duck,覆写Duck的display方法就可以了。
    可以看出,这样的设计对这样的需求是符合要求的。符合开闭原则---对修改关闭,对扩展开放。我们来看一下,这段代码怎么对修改关闭,对扩展开放了。在server端,当需要添加鸭子的时候,只需要继承Duck即可。不需要修改现有的任何代码。有人就问了,那client端呢?不是要改代码吗?(以前我也有这样的疑问)。同样是看了上面那幅Factory的图我豁然开朗。当区分开了client和server端后,知道了开闭原则是针对server端的。对于client端,你要调用server端新添加的鸭子当然要改源代码了,不然怎么调用?当然server端可以封装个Factory提供出来。源代码多多少少都是需要修改的。

    对于这样的设计呢!OO语言都是没什么太大的区别的,C++,Ruby实现都很类似了。

第一个平衡点:
    这里可能会有一些声音,说对于client来说,调用Duck的时候需要new才可以,所以要个Factory。这里可能就是斟酌的地方了。上面已经说了,模式是用来封装变化的。如果这几个Duck类都不会变化,那么client使用new,也没什么问题。而问题是你没办法保证这些Duck不会发生变化。从另一个角度来看,你也不能保证这些Duck一定就会发生变化,如果所有的地方不分青红皂白全用Factory,那么就会Factory类爆炸了吧。而如果都不用Factory类,如果Duck类发生了变化,那么当类的数量变化得很多时,修改就是个噩梦了吧。

第二个平衡点:
    接着呢!出现了WoodDuck了,他是不会quack的。继续上面的方式--继承。覆写quack方法,空实现即可。再来一个PaperDuck呢?继续继承?空实现?这里就出现了重复了。而这里应该又是一个平衡点了。HeadFirst是假设此类不会quack的Duck越来越多的话,重复代码就越来越多了,继而进行了重构。我觉得这里有几个问题
1.此处完全是在假设的前提下进行重构的。如果类没继续增加呢?
2.如果要重构,到底多少个类似的Duck出现时需要重构呢?
3.假设根据DRY原则,出现第二个的时候就进行重构是否有些劳师动众?如果Duck已经继承了100甚至更多的类了,而且类运行得很好,此时出现了WoodDuck和PaperDuck进行重构的话,风险有多大呢?
4.有测试呢!你能保证测试覆盖全面吗?!

    我们继续往下看!假设现在已经有100个类似RedDuck,GreenDuck的类继承了Duck了,测试类覆盖完全。此时出现了WoodDuck和PaperDuck类,这个时候再看HeadFirst里面的关于抽象出接口的实现方式,应该一眼就看出他的弊端了。抽象出一个Quackable接口,能Quack的就实现这个接口,不能Quack的就不实现这个接口。好吧,现在有100只鸭子能Quack,2只不能Quack,你如此重构看看。多了100个重复(谁让接口不能实现方法呢!)。。。知道什么叫吃力不讨好了吗?

    这时候C++笑了,让你不能多继承。看我,直接抽出Quack父类,谁能Quack就继承Quack。不能Quack的就不继承。
 
    Ruby也笑了,直接将quack放到Quack模块里面去,谁能quack就mixin Quack模块呗。

    再回到Java,这里就开始关注变化点!很明显,这里的不稳定因素是quack,有的鸭子能quack有的则不能,我们则把quack抽出来,独立为一个类。当需要quack的时候就设置这个quack即可。这就是策略模式。

第三个平衡点:
    策略模式应该也分为两种,我自己把它称作server端的策略和client端的策略。server端的策略就是将quack实例化在了相应的duck内部,client端直接实例化即可。而client端的策略就是在client端自己设置相应的quack到duck中去。很明显,client端的策略更灵活。但是这里不适用。看看这里已经有100多个类了,如果每个类都修改为client端的策略模式,那么修改量太大,收效也不明显。当然也可以混合使用,默认提供了server端的策略,也提供client端的策略,供client端灵活调用。

    对于server端的策略实现,我们和c++,ruby的实现比较一下。假如,GreenDuck的quack需要修改了,策略模式是将duck内的quack实例替换掉。c++是将其quack父类修改掉(这里的父类肯定也是有个继承关系的,都有共同的父类,否则client端怎么调用呢?)。而ruby呢,直接添加一个新的quack模块,替换原来那个模块就可以了。

    所以,c++的多继承,ruby的mixin也同样的解决了问题。但是,c++和ruby从语言特性级别解决了java需要使用设计模式才能解决的问题。当然了,c++,ruby也是能实现策略模式的。


总结:
1. 模式也不是乱用的。对于第一个平衡点,可能会导致Factory爆炸。对于第二个平衡点,可能会加大工作量和风险。怎么平衡,看项目,看需求,看个人经验。

2. 模式需要结合实际情况。像上面的例子,c++,ruby从语言级别就可以解决问题了,不需要再升级到模式的级别。

    前段时间看了一本书《怪诞行为学》,里面提到了一些关于暗示的例子,不知道用在这里合不合适。例子是这样的,一家出版社网站打印了如下的书籍价格:《书籍A》pdf版本:$59,《书籍A》实体书:$120,《书籍A》pdf+实体书:$120。多少人会买$120的实体书呢?选项2暗示了你,选项3比选项1要划算!而在这里,当我们学完OO,书上说继承怎么样怎么样好的时候,我们的心里其实已经受到了影响,有问题就继承。再学习了模式后,又一次被暗示了,有问题找模式。继承和模式不过是一种结构而已,有适用的地方,具体哪里适用,需要我们来掌握。怎么掌握?经验。。。。也许这就是毕业生和资深开发人员的本质区别吧。
分享到:
评论

相关推荐

    matlab开发-Brainstorm

    这些方法能估计出大脑皮层上引起MEG/EEG信号变化的潜在源位置。 6. **统计分析** 软件内置了统计分析工具,包括单样本、双样本t检验,非参数检验,以及多变量分析,如主成分分析(PCA)和线性判别分析(LDA)。...

    brainstorm-master

    人工智能领域的头脑风暴,机器学习,深度学习相关的头脑风暴

    brainstorm_210711

    在"brainstorm_210711"这个项目中,我们可能会遇到以下几个关键知识点: 1. **脑电图(EEG)技术**:EEG是研究大脑功能的重要工具,能够实时捕捉到大脑的动态变化。它广泛应用于临床诊断,如癫痫、脑损伤评估,以及...

    brainstorm_190429.zip

    《脑电图(EEG)数据处理与分析:基于Brainstorm 190429插件的MATLAB应用》 在神经科学研究中,脑电图(Electroencephalogram,简称EEG)是一种非侵入性的技术,用于记录大脑皮层的电活动。这些记录的数据通常需要...

    code-organising-brainstorm

    关于Laravel Laravel是一个具有表达力,优雅语法的Web应用程序框架。 我们认为,发展必须是一种令人愉快的,富有创造力的经历,才能真正实现。 Laravel通过减轻许多Web项目中使用的常见任务来减轻开发工作的痛苦,...

    BrainStorm Encrypter-开源

    使用河豚,es,des或cast5加密文件的工具,使用md5,sha和maturemd160进行文件哈希处理,使用gzip进行压缩以及漂亮的用户界面。 使用带有Qt框架的C ++创建,取决于QCA第三方Qt库和OpenSSL。

    开源多用途笔记应用 BrainStorm.zip

    BrainStorm是一款开源多用途笔记应用,适用于自由协作、原型设计、长微博、任务列表等。类似Google Keep。 标签:BrainStorm

    Brainstorm mp3 Catalog-开源

    "Brainstorm mp3 Catalog"是一款开源的音乐管理软件,专为整理和快速查找MP3文件而设计。这个程序的核心目标是帮助用户有效地管理和检索他们的数字音乐收藏,无论这些文件存储在固定驱动器还是可移动设备上。开源...

    Brainstorm software MEG, EEG, fNIRS, ECoG, sEEG and electrop

    通过以上知识点,我们可以看出Brainstorm软件的强大之处,它集成了多种电生理数据的分析功能,为神经科学研究提供了一站式的解决方案。无论是在基础研究还是临床应用中,都能看到其广泛的应用价值。

    matlabk-means源码-BrainStorm_CPP:cpp中的“头脑风暴优化”(BSO)核心。基于“煤矿运营风险管理分析”项目。摘自

    matlab k-means源码 BSO_CPP Brain Storm Optimazation (BSO) core in cpp. Based on the project 'Analysis of Risk Management for the Coal Mine Operations'. Translated from ...代码实时更新:

    brainstorm-app-redux-prototype:我在上海JS聚会上演示的头脑风暴应用程序的原型

    头脑风暴的应用程序原型 这些是在热重载环境中使用redux,react和d3进行头脑风暴的应用程序的开始。 在上海JS聚会上的演示中使用,以演示用于快速原型制作的redux。 演讲幻灯片在[.pptx]中(具有较短开发周期的快速...

    Brainstorm+Fieldtrip iEEG分区定位 示例代码.rar

    6. **统计分析**:在预处理后,可以进行时频分析、事件相关功率变化(ERP/ERF)计算,或者采用其他统计方法来探究不同条件下的大脑活动差异。 7. **可视化**:最后,通过Brainstorm和Fieldtrip的图形界面,可以生成...

    ck-brainstorm:课堂头脑风暴的CommonKnowledge

    ck-头脑风暴课堂头脑风暴的CommonKnowledge 该软件在 MIT 许可下可用 ( )版权所有 (C) 2014 、 、 - 多伦多大学安大略教育研究所 (OISE) 特此授予任何人免费获得本软件副本和相关文档文件(“软件”)的许可,不受...

    django-brainstorm::cross_mark: 已弃用的头脑风暴想法投票应用程序

    在 urls.py 的某处添加对brainstorm.urls的引用: (r'^', include('brainstorm.urls')), # something that looks like this登录到 django 管理员并创建一个子站点。 子站点有一个 slug、名称、描述、主题和许多其他...

    brainstorm_220911

    在功能连接分析方面,brainstorm_220911利用相关性、相位锁合值等指标,量化不同脑区间的同步性,从而揭示大脑网络的结构和动态变化。这在理解认知、情感及疾病状态下的大脑功能连接具有深远意义。 此外,...

    brainstorm-vue:vue前端 实践课作业前台

    头脑风暴-vue 一个 Vue.js 项目 构建设置 # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build ...

    brainstorm-primary-24963:这个react_native应用程序是使用Crowdbotics www.crowdbotics.com构建的

    Crowdbotics React Native支架 克隆此存储库后,您将需要安装依赖项: cd ProjectName yarn install 然后,您需要安装Podfile: cd ios pod install 安装所有依赖项以进行开发 请按照本指南进行操作,并为当前...

    Brainstorm:头脑风暴:用于MEG / EEG数据分析的开源应用程序-matlab开发

    Brainstorm是一款协作式开源应用程序,致力于分析脑部记录:MEG,EEG,fNIRS,ECoG,深度电极和动物电生理学。 我们的目标是使用MEG / EEG作为实验技术,与科学界共享一套全面的用户友好工具。 对于医生和研究人员而...

    Brainstorm工具箱中用于脑电文件时间分割的源知情分割算法matlab实现.rar

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程...

Global site tag (gtag.js) - Google Analytics