- 浏览: 546097 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
wa114d:
楼主工作几年了,好厉害
一个面试官对面试问题的分析 -
wobuxiaole:
Good,非常好
30岁前男人需要完成的事 -
小逗逗:
Good,非常好
30岁前男人需要完成的事 -
invincibleLiu:
好帖,要顶!(别投我隐藏啊,这是对BBS最原始一种支持)
Java:synchronized修饰符在静态方法与非静态方法上的区别 -
fayedShih:
第三题,不知道对不对
import java.util.con ...
企业牛逼面试题目 高手进来讨论答题
最难的不是理解设计模式,而是在实际项目中灵活应用它们,设计模式看似简单,用起来却不知从何下手。理论是用来实践的,实践才能出真理。
设计模式属于OO的一部分, Gof的23种模式只不过是设计模式的沧海一粟,不同的领域都会产生不同的设计模式,当然你也可以总结出自己的设计模式。
对于学习设计模式的方法,我们不需要一开始就花很长的时间把它们都读通读透,只要花个十几天每天两个小时对每个模式有个简单的印象就可以了,然后在实际工作中去应用,去把它们读通读透。其实,只要你的OO能力达到一定的程度,设计模式都是无师自通的。
学习设计模式几个重要的问题?
每一类设计模式都是为了解决某类特定的问题而产生的,要了解这些设计模式,就必须先清楚驱动这些模式产生的需求是什么?(即是它们具体解决什么样的问题),凡事只要我们搞清楚了需求(为什么),就能使我们更加专注于需求的实现。
每个模式的简单类图?学习设计模式,最重要的是在实际应用中去发现它们的好处,从而加深我们对设计模式的理解,自己以后才能在设计中更加OO,更加具有可维护性和可扩展性。那么为了避免遗忘,使用类图能够帮助我们最快的理解某个设计模式的精髓,当然,亲自动手做一本设计模式速查手册也不免是一个不错的学习方法。
每个设计模式的优点是什么?缺点又是什么?每个设计模式都是为了解决某一类问题而反复被人们总结、推敲凝练而成。那么除了此之外,它还具有其他一些优点,同时不可避免也会带来一些缺点,掌握这些能够帮助我们知道何时采用何种设计模式。
它著名的应用举例?java_api里面使用了很多设计模式,我们可以在里面找到这些模式的踪影,这对于理解某个模块的设计原理再好不过了。
OCP(开闭原则,对扩展开发,对修改关闭)
OCP原则就是在不修改源代码的情况下,设计方案能适应于各种扩展的需求(当然这是最理想的情况)。做到OCP有两点:抽象、对可变性封装。
抽象,java里给我们提供了两种途径,抽象类和接口,拿接口来说,它固定了子类的方法特征,所有实现它的子类必须实现具体的方法,这是一种约定,这种约定就是一种抽象层,我们认为它定义的方法特征预见了任何可能的扩展,因此,这种约束在任何情况下不得改变,那么,我们可以理解为:它是对修改的关闭,遵循了OCP的第二条原则。同时,由于子类实现接口的方法可以又不同的具体表现形式,这些不同的形式足以对系统产生不同的行为,因此,系统的设计对扩展是开放的,这就满足了OCP的第一条原则。
对可变性的封装原则,很早以前我知道了设计应该将不可变的和可变的分开,这里偏离一下主题,组件设计(也成框架设计)通常将可变性放在配置文件中(通常是文件系统,比如XML),将不可变性形成一套模板,或者是一套流程,通过解析配置文件,将业务逻辑载入系统,使系统按照用户需求的业务逻辑去运行,尽管业务逻辑经常变化,但是我们通过简单的配置文件就可以解决,这不得不说可变性和不可变形的分离的确值得我们好好思考。下面我们回到主题,对可变性的封装,将会驱使你找到系统中的可变因素,将它封装起来。怎么去封装?不是把你的可变因素散落在代码的很多角落里,它应该是封装成为一个类,同一种可变性的具体表现映射为同一个抽象类(或者接口)的不同子类。想象一下,我们经常说接口犹如插座,插座就是一种可变性,不同的可变性犹如不同的颜色,不同的大小等等,这些可变性我们应该把它们封装成不同子类里。继承应当被看做是封装变化的方法,而不当认为是从一般对象生成特殊对象的方法。我们通常认为凤凰是从鸟继承过来是因为凤凰是特殊的,其实不然,凤凰对象只是封装了鸟类的可变性因素,麻雀也是鸟,它明显区别于凤凰的可变性因素,因为麻雀永远不会变成凤凰。
里氏代换原则(LSP)
任何基类可以出现的地方,子类一定可以出现(反过来不成立)。
这好像是描述了继承的一种原则,确实,在实现继承的时候,我们尽量考虑一下,java编译器能够检查语法上对里氏代换原则的支持,但是并不能支持商业逻辑上的LSP。考虑一个比较著名的长方形与正方形的问题,它能帮助我们更加深刻的理解LSP原则。
通常,在数学上来看,正方形确实属于长方形的一种,依照这种思维,正方形继承于长方形也是自然不过了。但是别忘了,长方形的定义是什么?长方形的高小于等于长方形的宽,下面是针对LSP原则的一段测试代码:
Public void resize(Rectangle r){
While(r.getHeight() <= r.getWidth()){
r.setWidth(r.getWidth() + 1);
}
}
试试LSP原则在这段测试代码中成不成立:当传入的是一个长方形,它的意义在于增加长方形的高度直到不大于长方形的宽度为止,如果传入的是一个正方形,长方形的子类,想象一下,这段代码会出现什么样的结果,因为正方形的高永远等于长方形的宽,这个循环永远持续下去,直到栈溢出为止。
还有一个例子,java.util库中的properties继承于hashtable,但是properties是一种特殊的hashtable,它只接受String类型的key和value,而超类hashtable的key和value却能接受任何的数据类型。这意味着,LSP在它们之间并不成立,所以,这是一个反面教材,时刻提醒我们,该在什么情况下使用继承。
依赖倒换原则(DIP)
要依赖于抽象,不要依赖于具体实现。DIP跟另一种说法含义相近:面向接口编程。
不知道何时有“层”这个说法,尽管你不会一眼看出XX软件分为几个层,但是确实这样的分层是有理由的,分开即耦合度降低,各司其责。你可以想想你所在公司的管理制度,那是一个金字塔模型。上层是高层管理人员,它们下发的命令直接影响最底层的工人,而最底层工人具体的工作内容并不影响上面的高层管理人员。商务逻辑层不能依赖于实现层,公司的架构师要负责商务逻辑层的维护,底层coder负责实现层,公司不可能将这种逻辑依赖于实现层,不仅仅是coder和架构师的素质差别,更重要的是这根本就是一个错误的逻辑。
依赖倒换原则是COM、CORBA、JavaBean以及EJB等架构设计模型背后的基本原则。
依赖的种类:零耦合关系(两个类没有发生任何相互引用)、具体耦合关系(两个具体类直接引用对方的具体类型)、抽象耦合关系(两个类的引用利用其抽象类或者接口来实现,这是DIP的核心)。
怎么实现依赖倒换原则?实现层要依赖于抽象层,那么所有的商务逻辑必须在抽象层制定好,而且要更改实现层的实现方式时,必须保证抽象层不做任何改变。通过定义抽象类和接口来实现商务逻辑的集中控制,通过面向接口编程来保证当实现层改变实现方式时,抽象层的商务逻辑不做改变。针对接口编程意思是说:应当使用抽象类和接口进行变量的类型声明、参量的类型声明、方法的返还类型声明以及数据类型的转换等。
模板方法直观的为我们解释了通过定义抽象类和接口来实现商务逻辑的集中控制,一般通过继承抽象类来得到抽象类具体方法的使用权限,通常这些方法的定义需求是统一的商务逻辑定义。迭代子模式也做到了这点,它将公用的迭代功能定义为一个接口,实现了这个接口的集合类表示支持迭代,使用迭代子接口声明的类型,在实现过程中,当集合类发生改变时,它也能正常工作,这就是所谓的抽象耦合关系,只对迭代接口的引用。
坚持DIP原则会生产出大量的类,它们大多都是抽象类和接口,怎么处理好这种引用关系是值得思考的。另外,依赖倒换原则假定所有的具体类都是会变化的,但是实际情况中不总是这样,这需要我们实际情况实际解决。
接口隔离原则(ISP)
应当为客户端尽可能小的单独的接口,而不要提供大的总接口。
总觉得这个和“单一职责原则”很像,很多人都把它们分开来讲,我不想把它们分的太清楚,就当一种说法对另一种说法的诠释好了。
这个原则比较简单易懂,就是应该把接口细粒化,单位化,避免接口污染。这样做的好处也很明显,接口就相当于对外界的承诺,你愿意对外界承诺的更多还是更少呢?其次,从美学上讲,这是一个污染问题,虽然不可能将美学纳入设计原则中,但是鄙人就喜欢做什么都干干净净、优雅利落,是个典型的完美主义者。
虽然ISP很好理解,但是实际中却常常被忽略,我经常会在公司里的代码中看到一个总的大接口,更要命的是它是从很多单位化细粒化接口继承过来的,而往往消费这个接口需要实现它所有的方法,这就诱发程序员偷懒行为:将不需要实现的方法置为真空状态(即里面什么都不写)。
定义接口通常我们会以职责来划分,一个职责一个借口,不要将多种职责放在同一个接口里,评判的标准是:你的接口是否仅仅因为一种原因而改变?但是“职责”是一个没有标准化的术语,实际项目中,100个人可能有98个人都有不同的职责看法(另外两个人看了此文章)。下面我举几个简单的例子来展示当我们遇到问题时,接口隔离原则为我们带来的好处。
第一,如果我将两种或两种以上的职责都放在一个大的接口里,如果有一天我发现只需要其中的一种职责就可以了,那我们难道就强制它实现这个接口,然后将它不关心的职责真空化吗(就像上面说的,将不需要实现的方法置为真空状态,那就只能说明你这个接口包含了过多的职责)?
第二,如果哪天我们发现其中有一个职责改变了,那么我们是不是要忍着牵一发而动全身的悲剧而修改这个接口呢?比如考虑手机通信这个接口,它有拨打电话和挂电话等操作,姑且我们把它看做协议职责,它还有通话,回应等操作,姑且我们把它看做数据传输职责,现在我们的手机都是2G的,哪天换成了3G的,视频通话的,那不仅仅是语音数据的传输,还包括视频数据的传输,那是不是我们必须要改动这个接口了呢?想想一下,有多少个类实现了这个接口我们就相应的修改这些类,如果我们开始的时候就定义为两个接口,两个职责,这个时候是不是就只需修改实现了数据传输接口的类就行了呢?
第三,如果你在写一个API组件,需要对外界提供一个借口供二次开发人员使用,我想,你肯定不想对外界做出更多的承诺吧,那么,就把你的接口单一职责化吧。
总的来说,我觉得有一句话概括很帅气:“我单纯,所以我快乐”。简简单单,清晰明了,分工明确,细粒度低,何尝不好。这句话不仅仅适用于接口,对类和方法也适用,当然,具体情况视具体项目而定。比如考虑一个修改用户信息的方法,它根据传入的type不同,分为修改用户名,用户密码,用户…。这种方法细粒度怎样?如果这个方法出问题了,受影响的功能会波及修改用户名,修改用户密码,修改…如果你所有对用户信息的修改都放在了此方法里,那么所有的用户信息修改都将会受到影响,这不是开玩笑的。而且,这样的代码不是自解释性的,可读性也不好,也不够复用性。
举一个亲身经历的例子,我有一个需求,判断当前的设备实际类型(你可以认为是电信设备)和当前用户配置的网元类型是否一样,相信很多人都会去写一个返回true或false的方法,这是不恰当的。因为你这个方法包含了两个职责:1.得到设备实际类型2.比较当前的实际类型和用户配置的类型是否一样。所以应该只需定义一个方法即可,它仅仅是得到当前设备的实际类型,然后返回给你,你在主程序中再去判断。这样做的好处是,如果有一天,我需要在程序的另一个地方得到当前设备的实际类型,那么你写的这个方法就不能复用,必须再写一个这样的方法才行。这是多么的纠结啊,所以,我单纯,我快乐。
原则是死的,人是活的,凡事适当就好。
合成/聚合复用原则(CARP)
要尽量使用合成/聚合,而不是继承关系达到目的。
我不想去区分合成和聚合的区别。
通常如果你正在疑惑你该使用合成/聚合还是该使用继承时,我给你两种方法去判断:1.使用“Has-A”和“Is-A”来判断;
2.使用里氏代换原则来判断。
迪米特法则(LoD)
一个软件实体尽当尽可能少的与其他软件实体发生相互作用。
LoD表述:1.只与你直接的朋友通信;2.不要跟“陌生人”说话;3.每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
想象一下,如果你现在有件事情非常重要,需要托关系才能办成,正好你的朋友的朋友可以帮你办成此事,但是你跟他确是陌生人,你是自己直接找呢?还是你托你的朋友去找他呢?想必我们大家有个脑子的都会让朋友去托人办成此事,而且如果朋友发现他的朋友办不成此事时,它会找另外一个朋友,或者朋友的朋友,总之,这也算是解耦合的关系。
在我看来,软件最重要的是可维护性和良好的扩展性(需求频繁的变化是最要命的),可变性是任何软件维护最头疼的问题,有些还是灾难性的,如何在设计之初就预料到这些问题才是最为难能可贵的。大多数人认为这依赖于构架师的经验,这一点我比较赞同,但是预料到问题之后,怎么去封装这些问题,我想我们都应该掌握。
可变性的封装,拿什么去封装?java为我们提供了两种方式,接口和抽象类,在变化之处放置一个接口或者抽象类是一个不错的办法。看一看23种设计模式哪种设计模式没有用到接口和抽象类?我常常在想,你的设计模式,我的设计模式,大家的设计模式应当都遵循同一种原则。这种原则就是对不同的可变性进行封装,说到底,还是对可变性的封装。
设计模式属于OO的一部分, Gof的23种模式只不过是设计模式的沧海一粟,不同的领域都会产生不同的设计模式,当然你也可以总结出自己的设计模式。
对于学习设计模式的方法,我们不需要一开始就花很长的时间把它们都读通读透,只要花个十几天每天两个小时对每个模式有个简单的印象就可以了,然后在实际工作中去应用,去把它们读通读透。其实,只要你的OO能力达到一定的程度,设计模式都是无师自通的。
学习设计模式几个重要的问题?
每一类设计模式都是为了解决某类特定的问题而产生的,要了解这些设计模式,就必须先清楚驱动这些模式产生的需求是什么?(即是它们具体解决什么样的问题),凡事只要我们搞清楚了需求(为什么),就能使我们更加专注于需求的实现。
每个模式的简单类图?学习设计模式,最重要的是在实际应用中去发现它们的好处,从而加深我们对设计模式的理解,自己以后才能在设计中更加OO,更加具有可维护性和可扩展性。那么为了避免遗忘,使用类图能够帮助我们最快的理解某个设计模式的精髓,当然,亲自动手做一本设计模式速查手册也不免是一个不错的学习方法。
每个设计模式的优点是什么?缺点又是什么?每个设计模式都是为了解决某一类问题而反复被人们总结、推敲凝练而成。那么除了此之外,它还具有其他一些优点,同时不可避免也会带来一些缺点,掌握这些能够帮助我们知道何时采用何种设计模式。
它著名的应用举例?java_api里面使用了很多设计模式,我们可以在里面找到这些模式的踪影,这对于理解某个模块的设计原理再好不过了。
OCP(开闭原则,对扩展开发,对修改关闭)
OCP原则就是在不修改源代码的情况下,设计方案能适应于各种扩展的需求(当然这是最理想的情况)。做到OCP有两点:抽象、对可变性封装。
抽象,java里给我们提供了两种途径,抽象类和接口,拿接口来说,它固定了子类的方法特征,所有实现它的子类必须实现具体的方法,这是一种约定,这种约定就是一种抽象层,我们认为它定义的方法特征预见了任何可能的扩展,因此,这种约束在任何情况下不得改变,那么,我们可以理解为:它是对修改的关闭,遵循了OCP的第二条原则。同时,由于子类实现接口的方法可以又不同的具体表现形式,这些不同的形式足以对系统产生不同的行为,因此,系统的设计对扩展是开放的,这就满足了OCP的第一条原则。
对可变性的封装原则,很早以前我知道了设计应该将不可变的和可变的分开,这里偏离一下主题,组件设计(也成框架设计)通常将可变性放在配置文件中(通常是文件系统,比如XML),将不可变性形成一套模板,或者是一套流程,通过解析配置文件,将业务逻辑载入系统,使系统按照用户需求的业务逻辑去运行,尽管业务逻辑经常变化,但是我们通过简单的配置文件就可以解决,这不得不说可变性和不可变形的分离的确值得我们好好思考。下面我们回到主题,对可变性的封装,将会驱使你找到系统中的可变因素,将它封装起来。怎么去封装?不是把你的可变因素散落在代码的很多角落里,它应该是封装成为一个类,同一种可变性的具体表现映射为同一个抽象类(或者接口)的不同子类。想象一下,我们经常说接口犹如插座,插座就是一种可变性,不同的可变性犹如不同的颜色,不同的大小等等,这些可变性我们应该把它们封装成不同子类里。继承应当被看做是封装变化的方法,而不当认为是从一般对象生成特殊对象的方法。我们通常认为凤凰是从鸟继承过来是因为凤凰是特殊的,其实不然,凤凰对象只是封装了鸟类的可变性因素,麻雀也是鸟,它明显区别于凤凰的可变性因素,因为麻雀永远不会变成凤凰。
里氏代换原则(LSP)
任何基类可以出现的地方,子类一定可以出现(反过来不成立)。
这好像是描述了继承的一种原则,确实,在实现继承的时候,我们尽量考虑一下,java编译器能够检查语法上对里氏代换原则的支持,但是并不能支持商业逻辑上的LSP。考虑一个比较著名的长方形与正方形的问题,它能帮助我们更加深刻的理解LSP原则。
通常,在数学上来看,正方形确实属于长方形的一种,依照这种思维,正方形继承于长方形也是自然不过了。但是别忘了,长方形的定义是什么?长方形的高小于等于长方形的宽,下面是针对LSP原则的一段测试代码:
Public void resize(Rectangle r){
While(r.getHeight() <= r.getWidth()){
r.setWidth(r.getWidth() + 1);
}
}
试试LSP原则在这段测试代码中成不成立:当传入的是一个长方形,它的意义在于增加长方形的高度直到不大于长方形的宽度为止,如果传入的是一个正方形,长方形的子类,想象一下,这段代码会出现什么样的结果,因为正方形的高永远等于长方形的宽,这个循环永远持续下去,直到栈溢出为止。
还有一个例子,java.util库中的properties继承于hashtable,但是properties是一种特殊的hashtable,它只接受String类型的key和value,而超类hashtable的key和value却能接受任何的数据类型。这意味着,LSP在它们之间并不成立,所以,这是一个反面教材,时刻提醒我们,该在什么情况下使用继承。
依赖倒换原则(DIP)
要依赖于抽象,不要依赖于具体实现。DIP跟另一种说法含义相近:面向接口编程。
不知道何时有“层”这个说法,尽管你不会一眼看出XX软件分为几个层,但是确实这样的分层是有理由的,分开即耦合度降低,各司其责。你可以想想你所在公司的管理制度,那是一个金字塔模型。上层是高层管理人员,它们下发的命令直接影响最底层的工人,而最底层工人具体的工作内容并不影响上面的高层管理人员。商务逻辑层不能依赖于实现层,公司的架构师要负责商务逻辑层的维护,底层coder负责实现层,公司不可能将这种逻辑依赖于实现层,不仅仅是coder和架构师的素质差别,更重要的是这根本就是一个错误的逻辑。
依赖倒换原则是COM、CORBA、JavaBean以及EJB等架构设计模型背后的基本原则。
依赖的种类:零耦合关系(两个类没有发生任何相互引用)、具体耦合关系(两个具体类直接引用对方的具体类型)、抽象耦合关系(两个类的引用利用其抽象类或者接口来实现,这是DIP的核心)。
怎么实现依赖倒换原则?实现层要依赖于抽象层,那么所有的商务逻辑必须在抽象层制定好,而且要更改实现层的实现方式时,必须保证抽象层不做任何改变。通过定义抽象类和接口来实现商务逻辑的集中控制,通过面向接口编程来保证当实现层改变实现方式时,抽象层的商务逻辑不做改变。针对接口编程意思是说:应当使用抽象类和接口进行变量的类型声明、参量的类型声明、方法的返还类型声明以及数据类型的转换等。
模板方法直观的为我们解释了通过定义抽象类和接口来实现商务逻辑的集中控制,一般通过继承抽象类来得到抽象类具体方法的使用权限,通常这些方法的定义需求是统一的商务逻辑定义。迭代子模式也做到了这点,它将公用的迭代功能定义为一个接口,实现了这个接口的集合类表示支持迭代,使用迭代子接口声明的类型,在实现过程中,当集合类发生改变时,它也能正常工作,这就是所谓的抽象耦合关系,只对迭代接口的引用。
坚持DIP原则会生产出大量的类,它们大多都是抽象类和接口,怎么处理好这种引用关系是值得思考的。另外,依赖倒换原则假定所有的具体类都是会变化的,但是实际情况中不总是这样,这需要我们实际情况实际解决。
接口隔离原则(ISP)
应当为客户端尽可能小的单独的接口,而不要提供大的总接口。
总觉得这个和“单一职责原则”很像,很多人都把它们分开来讲,我不想把它们分的太清楚,就当一种说法对另一种说法的诠释好了。
这个原则比较简单易懂,就是应该把接口细粒化,单位化,避免接口污染。这样做的好处也很明显,接口就相当于对外界的承诺,你愿意对外界承诺的更多还是更少呢?其次,从美学上讲,这是一个污染问题,虽然不可能将美学纳入设计原则中,但是鄙人就喜欢做什么都干干净净、优雅利落,是个典型的完美主义者。
虽然ISP很好理解,但是实际中却常常被忽略,我经常会在公司里的代码中看到一个总的大接口,更要命的是它是从很多单位化细粒化接口继承过来的,而往往消费这个接口需要实现它所有的方法,这就诱发程序员偷懒行为:将不需要实现的方法置为真空状态(即里面什么都不写)。
定义接口通常我们会以职责来划分,一个职责一个借口,不要将多种职责放在同一个接口里,评判的标准是:你的接口是否仅仅因为一种原因而改变?但是“职责”是一个没有标准化的术语,实际项目中,100个人可能有98个人都有不同的职责看法(另外两个人看了此文章)。下面我举几个简单的例子来展示当我们遇到问题时,接口隔离原则为我们带来的好处。
第一,如果我将两种或两种以上的职责都放在一个大的接口里,如果有一天我发现只需要其中的一种职责就可以了,那我们难道就强制它实现这个接口,然后将它不关心的职责真空化吗(就像上面说的,将不需要实现的方法置为真空状态,那就只能说明你这个接口包含了过多的职责)?
第二,如果哪天我们发现其中有一个职责改变了,那么我们是不是要忍着牵一发而动全身的悲剧而修改这个接口呢?比如考虑手机通信这个接口,它有拨打电话和挂电话等操作,姑且我们把它看做协议职责,它还有通话,回应等操作,姑且我们把它看做数据传输职责,现在我们的手机都是2G的,哪天换成了3G的,视频通话的,那不仅仅是语音数据的传输,还包括视频数据的传输,那是不是我们必须要改动这个接口了呢?想想一下,有多少个类实现了这个接口我们就相应的修改这些类,如果我们开始的时候就定义为两个接口,两个职责,这个时候是不是就只需修改实现了数据传输接口的类就行了呢?
第三,如果你在写一个API组件,需要对外界提供一个借口供二次开发人员使用,我想,你肯定不想对外界做出更多的承诺吧,那么,就把你的接口单一职责化吧。
总的来说,我觉得有一句话概括很帅气:“我单纯,所以我快乐”。简简单单,清晰明了,分工明确,细粒度低,何尝不好。这句话不仅仅适用于接口,对类和方法也适用,当然,具体情况视具体项目而定。比如考虑一个修改用户信息的方法,它根据传入的type不同,分为修改用户名,用户密码,用户…。这种方法细粒度怎样?如果这个方法出问题了,受影响的功能会波及修改用户名,修改用户密码,修改…如果你所有对用户信息的修改都放在了此方法里,那么所有的用户信息修改都将会受到影响,这不是开玩笑的。而且,这样的代码不是自解释性的,可读性也不好,也不够复用性。
举一个亲身经历的例子,我有一个需求,判断当前的设备实际类型(你可以认为是电信设备)和当前用户配置的网元类型是否一样,相信很多人都会去写一个返回true或false的方法,这是不恰当的。因为你这个方法包含了两个职责:1.得到设备实际类型2.比较当前的实际类型和用户配置的类型是否一样。所以应该只需定义一个方法即可,它仅仅是得到当前设备的实际类型,然后返回给你,你在主程序中再去判断。这样做的好处是,如果有一天,我需要在程序的另一个地方得到当前设备的实际类型,那么你写的这个方法就不能复用,必须再写一个这样的方法才行。这是多么的纠结啊,所以,我单纯,我快乐。
原则是死的,人是活的,凡事适当就好。
合成/聚合复用原则(CARP)
要尽量使用合成/聚合,而不是继承关系达到目的。
我不想去区分合成和聚合的区别。
通常如果你正在疑惑你该使用合成/聚合还是该使用继承时,我给你两种方法去判断:1.使用“Has-A”和“Is-A”来判断;
2.使用里氏代换原则来判断。
迪米特法则(LoD)
一个软件实体尽当尽可能少的与其他软件实体发生相互作用。
LoD表述:1.只与你直接的朋友通信;2.不要跟“陌生人”说话;3.每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
想象一下,如果你现在有件事情非常重要,需要托关系才能办成,正好你的朋友的朋友可以帮你办成此事,但是你跟他确是陌生人,你是自己直接找呢?还是你托你的朋友去找他呢?想必我们大家有个脑子的都会让朋友去托人办成此事,而且如果朋友发现他的朋友办不成此事时,它会找另外一个朋友,或者朋友的朋友,总之,这也算是解耦合的关系。
在我看来,软件最重要的是可维护性和良好的扩展性(需求频繁的变化是最要命的),可变性是任何软件维护最头疼的问题,有些还是灾难性的,如何在设计之初就预料到这些问题才是最为难能可贵的。大多数人认为这依赖于构架师的经验,这一点我比较赞同,但是预料到问题之后,怎么去封装这些问题,我想我们都应该掌握。
可变性的封装,拿什么去封装?java为我们提供了两种方式,接口和抽象类,在变化之处放置一个接口或者抽象类是一个不错的办法。看一看23种设计模式哪种设计模式没有用到接口和抽象类?我常常在想,你的设计模式,我的设计模式,大家的设计模式应当都遵循同一种原则。这种原则就是对不同的可变性进行封装,说到底,还是对可变性的封装。
发表评论
-
MySQL的sql查询语句优化
2017-02-24 13:51 576mysql中优化sql语句查询的30种方法: 1.对查询进行优 ... -
性能调优学习笔记zz
2011-09-02 12:42 16191、性能调优的步骤 1.1、衡量系统现状 包括请求次数、响应时 ... -
海量数据的删除策略
2011-03-13 12:12 869编辑:http://www.iteye.com/t ... -
面向海量服务的设计原则和策略总结
2011-03-04 13:10 1043来自:http://ayufox.iteye.co ... -
如何学习开源框架?
2011-03-04 10:26 889作者:kiral 链接地址:http://kiral.ite ... -
10个影响性能的问题
2011-02-24 15:37 994http://waterfox.iteye.com/blog/ ... -
web运维的可扩展性考虑 zz
2011-02-24 15:30 9471.优化 不要过度优化。 ... -
学习技术的三部曲:WHAT、HOW、WHY
2011-02-24 14:34 882近几天有些网友在邮件 ... -
MVC框架的困惑
2011-02-24 13:54 866现在许许多多的初学者和程序员,都在趋之若鹜地学习Web开发的宝 ... -
如何快速的呈现我们的网页(zz)
2011-02-24 12:49 1108一.我们需达解决的麻烦 ... -
重构实践之一(zz)
2011-02-24 10:43 887昨天看了一下CSDN举办的 ... -
经验体会 (zz)
2011-02-24 10:34 8241.重构,伴随着整个 ... -
软件产品质量和代码质量(zz)
2011-02-24 10:00 927软件质量这个概念很模糊,它即可以代表一款可运行软件的优劣,也可 ... -
质量是免费的之【人的质量】(zz)
2011-02-24 09:42 888http://cantellow.iteye.com/blog ... -
产品之路的随想(社区版) zz
2011-02-21 13:55 1167转载于:http://www.iteye.com/topic/ ... -
zz发中的小心得
2011-02-21 12:34 1028总结一下自己在开发上 ... -
八个改善Java遗留系统的技巧
2010-11-02 18:17 827你没看错,就是这个题 ... -
写给我的团队-代码篇
2009-07-17 14:39 1093看了neora的大作写给我的团队 ,颇受启发,在这里我借花献佛 ...
相关推荐
根据给定文件的信息,我们可以详细地探讨24种设计模式及其相关的6大设计原则。 ### 一、24种设计模式 #### 1. **策略模式(Strategy Pattern)** - **定义**:定义一系列的算法,把它们一个个封装起来,并且使...
GRASP模式在面向对象设计中起到了至关重要的作用,是学习设计模式之前必须了解的基础知识。 GRASP模式中的两种重要原则: 1. **信息专家** (Information Expert):当一个类拥有完成某个职责所需的所有信息时,该...
以下是对几种设计模式的详细理解和应用。 1. 工厂模式 工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在这个模式中,一个工厂类负责创建对象,而客户端无需知道创建的具体过程。工厂类可以根据...
3. **设计原则**:学习设计模式前,理解SOLID原则是必要的。SOLID代表单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则是指导良好设计的基础,有助于写出更灵活、可维护的代码。 4. **...
### 24种Java设计模式介绍与6大设计原则 #### 第1章 策略模式(Strategy Pattern) 策略模式是一种行为型设计模式,它定义了一系列的算法,并将每一个算法封装起来,使得它们可以互相替换。策略模式让算法的变化...
在这种情况下,数据共享微服务设计模式可以用于解决单体应用到微服务架构的过渡阶段中可能出现的数据重复和不一致问题。 数据共享微服务设计模式是一种解决微服务架构中数据共享问题的设计模式。在这种模式下,部分...
设计模式是软件开发领域中一种重要的概念,它们是解决问题的模板,可以被反复使用。设计模式主要分为三类:创建型模式、结构型模式和行为型模式。每一种模式都代表着一种特定问题的最佳解决方案。下面是23种设计模式...
Java23种设计模式最牛逼的例子
│ 2.1设计模式的六大原则你知道多少.mp4 │ 2.3多个业务场景浏览-设计模式使用前后的区别.mp4 │ ├─第三章 创建型设计模式-单例设计模式和应用 │ 3.1江湖传言里的设计模式-单例设计模式.mp4 │ 3.2代码...
在探讨设计模式的入门知识之前,我们需要对面向对象(Object-Oriented,OO)和面向过程(Procedure-Oriented)的编程思想有所了解。面向过程的编程是以事件为中心的,注重于具体的操作步骤,如C语言就是面向过程编程...
### 设计模式精解——GoF 23种设计模式解析及C++实现源码 #### 0. 引言 设计模式是软件工程领域的一个重要概念,它提供了一种解决常见问题的方法论。GoF(Gang of Four)所提出的23种设计模式被视为面向对象设计的...
GoF 的设计模式表面上好象也是一种具体的"技术",而且新的设计模式不断在出现,设计模式自有其自己的发展轨道,而这 些好象和 J2EE .Net 等技术也无关! 实际上,GoF 的设计模式并不是一种具体"技术",它讲述的是...
有经验的设计师倾向于复用之前成功的设计模式,而不是每次都重新发明轮子。这不仅可以提高开发效率,还能确保设计的质量。 #### 三、设计模式的结构 每个设计模式通常包括以下几个核心部分: 1. **模式名称**...
GoF(Gang of Four)所提出的23种设计模式,被认为是面向对象编程中最核心的设计原则之一。这些模式可以帮助开发者解决常见的编程问题,并提高代码的可复用性和可维护性。 #### 创建型模式 创建型模式关注的是对象...
本总结文档涵盖了所有的设计模式,并对常用的模式进行了详细解释,非常适合程序员在面试前快速复习以及日常学习使用。 文档中提到的六大设计原则,包括单一职责原则、里氏替换原则、接口隔离原则、迪米特法则、开闭...
第三本"23种JAVA设计模式和15种J2EE设计模式.pdf"在前一本的基础上增加了J2EE(Java 2 Platform, Enterprise Edition)环境下的设计模式。J2EE设计模式更专注于分布式、多层架构的应用,如 Session Bean模式、Entity...