• 山猫
    2019-12-16
    我同意老师通过这个例子简单的描述开闭原则。但如果项目初始就对button按钮需要进行这么复杂的设计,那么这个项目后期的维护成本也是相当之高。

    作者回复: 是否要使用各种设计模式设计一个非常灵活的程序,主要是看你的需求场景,而不是看项目的阶段。

    如果你的场景就是需要这么灵活,就是要各种复用,应对各种变更,那么你一开始就应该这样设计。

    如果你的场景根本不需要一个可复用的button,那么就不需要这样设计。

    关键还是看场景。

    但是场景也会变化,一开始不需要复用,但是后来又需要复用了,那么在在需要复用的第一个场景,就重构代码,而不是等将来维护困难局面hold不住了再重构。

    ps 如果你习惯了这种灵活的设计,你会觉得这种设计并不复杂。对于软件开发而言,复杂的永远是业务逻辑,而不是设计模式。设计模式是可重复的,可重复的东西即使看起来复杂,熟悉了就会觉得很简单。

    pps 看起来复杂的设计模式就是用来解决维护困难问题的,正确使用设计模式,看起来复杂了,其实维护简单了,因为关系和边界更清晰了,你不需要在一堆强耦合的代码里搅来搅去。真正维护成本高的其实是你所谓的简单的设计,牵一发动全身,稍不注意就是各种bug。

    ppps 重要的话再说一次:
    关键还是看场景。

    没有银弹,没有一种必然就是好的设计方案,能理解场景的才是真·高手。

    
     36
  • 陈小龙 Cheney
    2019-12-17
    希望老师给出几个阶段的代码. 方便对着代码对比学习. 直接看文字感觉抽象模糊了.
    
     6
  • Jesse
    2019-12-16
    思考题
    匿名内部类,已经数字按钮注册的listener其实就是DigitButtonDailerAdepter适配器的实现,sendButton中注册的listener其实就是SendButtonDailerAdepter适配器的实现。
     1
     4
  • Roy Liang
    2019-12-17
    /*适配器模式*/
    public class Button {
        private ButtonServer server;
        private int token;

        public Button(int token, ButtonServer server) {
            this.token = token;
            this.server = server;
        }

        public void press() {
            server.buttonPressed(token);
        }
    }

    public interface ButtonServer {
        void buttonPressed(int token);
    }

    public class Dialer {
        public final static int SEND_BUTTON = -99;

        public void enterDigit(int digit) {
            System.out.println("enter digit: " + digit);
        }

        public void dial() {
            System.out.println("dialing...");
        }
    }

    public class DigitButtonDialerAdapter implements ButtonServer {
        private Dialer dailer = new Dialer();

        @Override
        public void buttonPressed(int token) {
            dailer.enterDigit(token);
        }
    }

    public class SendButtonDialerAdapter implements ButtonServer {
        private Dialer dialer = new Dialer();

        @Override
        public void buttonPressed(int token) {
            dialer.dial();
        }
    }
    展开
    
     3
  • Jonathan Chan
    2019-12-16
    求老师后续给出完整代码学习!
     1
     3
  • QQ怪
    2019-12-29
    看了2遍,敲了一遍,https://github.com/xqq1994/DesignPatternLearn
    
     2
  • 山猫
    2019-12-19
    没有想到我的评论老师会有那么多文字进行评论,也没有想到会有那么多赞。

    看了老师的评论,其实我也知道这个和所处的场景有很大关系。

    在项目开发过程初期,有很多东西是想不到的。譬如一个登陆页面,

    第一版可能就是简单的账号口令登陆,
    第二版可能就需要加上第三方登陆,
    再往后可能需要分类型登陆,
    在过一段时间可能有客户端的登陆。

    还有一种可能就是这个项目真的就这一个登陆,其他的登录方式又采取微服务的办法。

    有时候真的是看每个工程师的经验,还有对客户的熟悉程度。做开发好多年了,过度开发我也写过,预估不足我也写过。

    需求每天都在变,虽然设计模式能够照顾到大多数的需求变化,但总有坑死人的客户和打不死的需求。

    祝每个开发者好好学习技术,祝每个开发者都不要遇到坑死人的项目。

    疯了,累了,痛了,人间喜剧@_@
    展开
     1
     2
  • Paul Shan
    2019-12-16
    开闭原则是移除底层的if else,取而代之的是上层的类结构。不过,我个人以为一开始的if else, 甚至switch 也没什么不妥的,毕竟代码简单直接。引入了很多类,读代码也是负担,而且也很难预料到哪些修改是必要的。当if else数量多于一定的数目,再开始重构。 不知道李老师如何看待这种观点。

    作者回复: 当你准备写第一个else的时候,就说明你的代码即将陷入僵化、牢固和脆弱,而且为将来的需求变更引入了一个糟糕的“设计模式”。

    如果其他人接手你的代码,他有两个选择,要么继续写更多的else以应对需求变更;要么心理暗骂一声然后重构你的代码。你希望他选择哪个?

     4
     1
  • Yes
    2020-01-27
    我不是想找茬,我就想知道以上的代码怎么对说好的加“*” 和“#” 开闭

    作者回复: starButton = new Button();
    starButton.addListener(
      new ButtonListener() {
         public void buttonPressed() {
          dialer.enterDigit(STAR);
      }
     }
    );

    
    
  • 虢國技醬
    2020-01-20
    “开闭原则可以说是软件设计原则的原则,是软件设计的核心原则,其他的设计原则更偏向技术性,具有技术性的指导意义,而开闭原则是方向性的,在软件设计的过程中,应该时刻以开闭原则指导、审视自己的设计:当需求变更的时候,现在的设计能否不修改代码就可以实现功能的扩展?如果不是,那么就应该进一步使用其他的设计原则和设计模式去重新设计。”
    读的过程中一直有这种感觉:开闭原则可能是软件设计和实现时最重要的原则;果然和老师最后的总结一样。👍

    作者回复: 👍

    
    
  • InfoQ_e077cb303519
    2020-01-19
    看到评论突然想说一下,不要舍本逐末,直接写逻辑固然快捷,但是当业务变更和扩展时,或者有人维护你的代码时,比骂人还难受的感觉,就是恶心想吐
    
    
  • 睡浴缸的人
    2020-01-18
    封装变化点,这个真的就要看工程师的经验和抽象能力了。所幸,目前正在向这方面努力~
    
    
  • TH
    2019-12-31
    适配器的例子中使用了两个适配器类,这两个类都会接收任意buttonPressed吗?还是说由谁来控制具体使用哪个适配器?可不可以把两个适配器合为一个呢?

    作者回复: 不同button按下调用不同的adapter,参考文中代码。

    
    
  • QQ怪
    2019-12-29
    课后问题:
    两个适配器全在Phone的构造函数中以匿名内部类形式存在
    
    
  • 灰灰
    2019-12-23
    打卡
    
    
  • 张希音
    2019-12-23
    以前感觉面试的时候问设计模式都是为了面试而设计的问题,现在经过这么一分析,终于明白了设计模式的重要性,是时候去隔壁补补设计模式的知识漏洞了
    
    
  • 诗泽
    2019-12-19
    通俗的说就是,软件功能可以扩展,但是软件实体不可以被修改

    如果这个phone 之前是没有摄像头,现在加了一个摄像头那phone 这个实体要不要修改呢?🤔
    
    
  • 唐二毛
    2019-12-19
    有一点想不通,在adapter里面还是需要判断呀?这并没有 达到老师说的 避免做switch/if判断的效果,而且判断的逻辑一点不少,还无端弄出这么多类,有必要非得这么做吗?

    作者回复: Adapter不需要判断,请看思考题

    
    
  • Roy Liang
    2019-12-18
    依葫芦画瓢,应用观察者模式写了一个密码箱类
    public class CodeCase {
        private Dialer dialer;
        private Button[] digitButtons;
        private Button lockButton;

        public CodeCase() {
            dialer = new Dialer();
            digitButtons = new Button[10];
            for (int i = 0; i < digitButtons.length; i++) {
                final int digit = i;
                digitButtons[i] = new Button();
                digitButtons[i].addListener(() -> dialer.enterDigit(digit));
            }
            lockButton = new Button();
            lockButton.addListener(() -> dialer.lock());
        }

        public static void main(String[] args) {
            CodeCase codeCase = new CodeCase();
            codeCase.digitButtons[1].press();
            codeCase.digitButtons[2].press();
            codeCase.digitButtons[3].press();
            codeCase.digitButtons[4].press();
            codeCase.lockButton.press();
        }
    }
    展开
    
    
  • 草原上的奔跑
    2019-12-17
    想问下老师,在写设计文档的时候,系统,子系统,组件,分别映射到代码的什么层面(类很好理解);还有老师的设计文档中没有业务架构图和技术架构图,老师对这两种架构图是怎么理解的,设计文档中需要加入吗?希望老师给予解答

    作者回复: 组件就是jar,dll这些。子系统和系统就是可运行的完整程序,一个或者多个war。

    不管是什么架构图,只要你觉得有助于你描述你的架构设计,都可以加入设计文档中。


    
    
我们在线,来聊聊吧