面向对象设计原则 - 通过一个小练习体会设计之美
2020-06-22 11:51:59 0 举报
面向对象设计原则
作者其他创作
大纲/内容
Scenarios场景视图(面向用户场景的用例图)
SendButton
+ press
Dialler拨号器
public class Phone { private Dialer dialer; private Button[] digitButtons; private Button sendButton; public Phone() { dialer = new Dialer(); digitButtons = new Button[10]; for (int i = 0; i < digitButtons.length; i++) { digitButtons[i] = new Button(); final int digit = i; digitButtons[i].addListener(new ButtonListener() { public void buttonPressed() { dialer.enterDigit(digit); } }); } sendButton = new Button(); sendButton.addListener(new ButtonListener() { public void buttonPressed() { dialer.dial(); } });} public static void main(String[] args) { Phone phone = new Phone(); phone.digitButtons[9].press(); phone.digitButtons[1].press(); phone.digitButtons[1].press(); phone.sendButton.press(); }}
Speaker按键音
Programmers 程序员 Software management 软件管理
违反里氏替换原则例子:
DigitButtonDialerAdapter
+ buttonPressed
HashTable
5:connect
【关于产品经理】 所以说:架构师不能说服产品经理,就很被动,东西越做越难以维护。甚至产品经理需求都搞不清楚就交给开发去做【关于架构师权衡】技术优先,还是业务优先。从自己和跟随你的工程师来说,技术优先,才有成长;从公司角度来说,业务优先,给公司赚钱。架构师要权衡,否则有公司没人才不行,有技术公司没赚钱,照样人走茶凉【关于CTO】cto就是卖人设,通过人,项目,写书,博客,演讲塑造人设。因为决定你是cto的人不懂技术
Dialer
Integrators 集成商Performance 性能Scalability 可伸缩性
正方形
+ getWidth() : double+ getHeight() : double+ getSide() : double+ setSide()
Physical View物理视图
提醒
改造为
Process View过程视图
Button
1:digit(n)
问题:对Button改进后,Button可以扩展了。但:还依赖Dialer,会受到Dialer改动的影响
:Dialler
委托
Properties
推荐
里氏替换原则 -自我总结:1. 如何继承,才满足里氏替换原则尽量不要重写父类,继承后扩展新功能即可。如果非要重写,则契约大于父类方法,抛出父类抛出的子类异常2. 如果不满足里氏替换原则,则不能继承,此时怎么办?如黑马不能继承白马,正方形不能继承长方形抽取公共特征到基类,或使用组合代替继承
:Radio
ButtonServer
+ buttonPressed(token)
类图
继续改进Button:策略模式,只关心press,不关心具体的策略(dial或enterDigit)
+ getSide() : double+ setSide()
+ enterDigit()+ dial()
书《敏捷软件开发》理念:不讲软件开发过程如何敏捷,而是强调“软件设计”本身必须首先是敏捷的,设计的程序附上以上设计原则,才能有敏捷的基础,否则系统越做越烂,腐化严重,难易维护和适应变化,再好的敏捷过程管理都敏捷不起来。
2:displayDigit(n)
:Screen
问题:改进后,Button和Dialer都可以扩展了。但:增加新功能:当button按下,点亮灯当前设计如何实现?1. 首先开发一个灯类,这是必然的。2. 增加一个灯的适配器,这个也是必然的(为什么不能让灯直接实现ButtonServer吗?答案是no。因为这样不能实现同时拨号和亮灯的功能,只有适配器可以做到)那有没有办法去掉灯的适配器,答案是观察者模式
Screen屏幕
dial
附:UML4+1视图
enterDigit
把公共get方法提取到基类
长方形
+ setWidth() + setHeight()
System engineers 系统工程师 Topology 拓扑 Communications 通讯
Button按键
Stack
+ enterDigit()+ dial()+ buttonPress()
:Button
6:displayInUse
DigitButton
优秀的程序员 vs 一般程序员:优秀程序员,欢迎需求变更,因为程序设计就是为变化而设计的一般程序员:害怕需求变更,因为变化总让他手足无措,拆东墙补西墙
里氏替换原则:从java语法角度看,意味着:1. 子类一定拥有父类的整个接口2. 子类的访问控制不能比父类更严格父类要求Object,子类是String就不行父类是protected,子类是private就不行从契约的角度看:子类的契约不能比父类更严格例如:正方形长宽相等比长方形更严格,不能是长方形的子类例如:Properties的String比HashTable的Object更严格,不能是他的子类思考:子类抛出的异常,应该是父类抛出异常的子类还是父类 (答案:子类)如何父类抛出Exception,使用方捕获Exception,替换成子类后抛出参数无效异常,可以被捕获,如果反过来,父类抛出非法参数异常,使用方捕获它并处理,替换成子类,抛出了数组越界异常,肯定捕获不到
举个例子:新需求来了,好的设计,只需增加一个类,单元测试也简单,产生bug的几率也很小,能快速开发测试和上线
3:tone(n)
ButtonListener
send:Button
Vector
灯
:Speaker
根据UML图想象程序代码:
- List<ButtonListener>
+ addListener(ButtonListener l)
1. 高层Controller 依赖底层service接口,不复合依赖倒置原则,不满足第二条2. 应该高层Controller定义接口需求,我就调用这个接口,而不关心service如何实现,依赖倒置,我不在依赖实现,控制反转,交给框架自动帮我注入实现类所以前后端分离,最好是前端定义接口,后端实现----理论上考虑接口的复用性和结构合理性----实际上后端来定义。本质上前端依赖的是接口就ok了
传统编程 :
方形
+ getWidth() : double+ getHeight() : double
End-user 终端用户Functional 功能
但上面,NIO仍然是同步的,需要线程定时轮询。想要更高效,就是异步处理,定义好方法,IO完成后去调用处理,不需要等待被轮询到后自己处理,而是由对方执行响应式编程:就是异步处理方式
问题:对Button改进后,Button可以扩展了。而且也不受Dialer的影响但:Dialer类需要根据token判断执行,如何改进Dialer?
人
+ 骑马
改进Dialer:观察者模式,Dialer和灯各自观察按钮事件, 不需要适配器
马
代码重出现if/else就是问题,只要扩展就要改代码。使用策略模式拆解可以只有if,但不能有else
Radio无线电
Development View开发视图
设计一个控制电话拨号的软件。下面是一个“拔打电话\
依赖倒置原则:1. 依赖抽象,不要依赖实现|2. 高层不要依赖底层框架的核心:好莱坞原则:Don't call me I will call you前端仅定义接口,传入参数,由框架注入接口实现,并调用接口 --- 响应式编程模式Reactive,如RxJava
面向对象设计原则:1、单一职责原则:实现类尽量小,一个类只有一个改变的原因,拆出去不属于此类的功能,本身n多方法属于此类,缺一不可,则是ok的,n多方法对于类来说是单一职责的2、开闭原则: 讲述解耦3、里氏替换原则:讲述是否适合继承4、依赖倒转原则:讲述框架设计原理,该原则其实就是策略模式,好莱坞原则,Dont call me i will call you。 让我调用你,改为我定义策略接口,然后autowire一下,代表我需要这样一个实现类,控制交给框架,框架来注入这个实现。所以说依赖倒转IOC,需要依赖注入的。5、接口隔离原则:一个接口有10多个方法,但使用方只用其一,就不适合实现此借口,需要拆分胖接口。对比“单一职责原则”,类拆不开,接口也要注意拆分,否则实现类不能灵活6、合成复用原则7、迪米特法则-又称最少知识原则
2 另一个办法:不能继承,就用组合
想象代码(Phone类-用来组装)
+ getWidth() : double+ getHeight() : double+ setWidth() + setHeight()
面对变化,只需增加新的类,旧的类不需要改
白马
设计之美:后面不管按钮按下如何扩展,增加ButtonListener实现类就行了如果灯也需要不同按钮亮不同颜色,增加灯的适配器即可消除ifelse
1. 需要一匹马,给你一匹黑马当然可以2. 需要一匹黑马,给你一匹马(可能是白马),当然不可以什么样的马不能继承父马?答案:小马原因:(违反里氏替换原则:人骑的马不能替换成小马,所以小马不能继承马,也就是小马不属于马的子类,因为人骑的马暗含必须是成年马)
开闭原则:对扩展开放,对修改关闭
里氏替换原则:1. 子类型必须能够替换掉父类型
如何解决违反里氏替换原则的问题:1. 最简单办法:抽象一个公共基类,把公共特性放进去
服务器为每一个请求创建一个线程,执行数据库操作和等待请求数据,都阻塞,当高并发的时候,服务器的最大线程是固定的,而且都阻塞在哪里,没干正事,新的用户请求进不来,超时其实springBoot内置tomcat已经是NIO模型了,是非阻塞的多路复用,使用少量线程应对大量并发请求瓶颈来到数据库这一端:数据库服务器是IO密集型,cpu能力较弱,不适合NIO,所以缓存是优化手段,减少访问数据库的次数另外考虑Nosql,SSd,磁盘顺序读写,通过消息队列进行异步处理等优化IO手段。数据库读写分离,分库分表继续优化:单机已最大化,如果高并发压力很大,考虑集群+负载均衡+分布式继续优化:动静分离,静态资源直接NG,动态资源ETag,静态化继续优化:带宽优化,分离图片服务器拆分流量继续优化:网络链路优化,CDN内容分发继续优化:本次存储、浏览器缓存,请求资源合并,压缩,响应式请求继续优化:Http协议优化,keepAlived,HTTP2.0多路复用
黑马
改进Button:让Button可扩展
4:send
Logical View逻辑视图
合作图(协作图、通讯图)
改进Dialer:适配器模式,Dialer不变,中间添加适配器
0 条评论
回复 删除
下一页