- 如果想成为一名更优秀的软件设计师, 了解优秀软件设计的演变过程比学习优秀的设计本身更有价值, 因为设计的演变过程蕴藏大智. <<重构与模式>>
- 重要的不是你将来会不会用到这些设计模式, 而是通过这些模式让你找到”封转变化”, “对象间松散耦合”, “针对接口编程”的感觉, 从而设计出更易维护, 易扩展, 易复用, 灵活性好的程.
- 聚合关系(Aggregation): 表示一种弱的”拥有”关系, 体现的是A对象可以包含B对象, 但B对象不属于A组合的一部分(如雁和雁群).
- 组合关系(Composition): 表示一种强的”拥有”关系, 体现了严格的部分与整体的关系, 部分和整体的生命周期一. (鸟和翅膀)
- 当你发现你已经可以掌握次程序语言之后, 你可以选择:
向上延伸: 学习面向对象设计, 设计模式, 反射以及软件工. 让自己具备做大型项目的能.
2. 向下延伸: 深入了解语言的内部运行机制, 例如数据结构, 操作系统的原理, 计算机组成与结.
3. 向旁延伸: 学习不同应用领域的API, 例如: 多媒体, 数据库, 分布式运.
## JAVA变量的种类:
```java
Class MyClass{
static int a;
int b ;
public static void myMethod(int c){
try{
int d;
}catch(Exception e){}
}
public MyClass(int f){
int[] g = new int[10];
}
```
类变量: 声明在类内, 方法之外, 且使用了static修饰的变例如变量.
成员变量: 声明在类内, 方法之外, 且未使用static修饰的变量, 如变量.
方法参数: 声明在方法的小括号内的变量, 如变量.
狭义的局部变量: 声明在方法内的变量, 如变量d,异常处理参数: 声明在catch小括号内的变量, 如变量.
构造方法参数: 声明在构造方法的小括号内的变量, 如变量.
数组元素: 数组的元素没有识别名称, 必须透过数组和索引来组织, 如变量g[0].
类与类之间的关系:
1)关联(Association)关系:表示一个类‘知道’另一个类。比如,企鹅和气候之间的关系。关联关系用实线箭头表示。
2)聚合(Aggregation)关系:表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。比如,雁群和大雁的关系。聚合关系用空心的菱形+实线箭头来表示。
3)组合(Composition)关系:表示一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。比如,鸟和其翅膀就是组合关系。组合关系用实心的菱形+实线箭头来表示。
4)依赖(Dependency)关系:比如动物要有生命,需要有氧气,水和食物。那么,动物依赖于氧气,水和食物类。依赖关系用虚线箭头来表示。
格言:编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,要时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用,只有这样才可以真正得到提高,写出优雅的代码真的是一件很爽的事情!
面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的抽象集合才是类。
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式是一种定义一系列算法的方法,从概念上看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以不同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合。
优点:1)策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
2)简化了单元测试,因为每个算法都有自己的类,可以通过自己的借口单独测试。
3)策略模式封装了变化。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。比如哦罗斯方块的上下左右控制职责。
软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。其实要去判断是否应该分离出类来,也不难,那就是:如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,此时,就应该考虑类的职责分离。
开放-封闭原则:就是说软件实体(类,模块,方法等)应该可以扩展,但是不可修改。
这个原则其实是有两个特征,一个是:对于扩展是开放的(open for extension),另一个是:对于更改是封闭的(closed for modification).
面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这是‘开放-关闭原则’的精神所在。
组合聚合复用原则;
开放-封闭原则是面向对象设计的核心所在。
依赖倒转原则:
1)高层模块不应该依赖于底层模块。两个都应该依赖抽象。
2)抽象不应该依赖细节。细节因该依赖抽象。
(针对接口编程,不要对实现编程)
1)迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的 话, 可以通过第三者转发这个调用。
里氏代换原则:子类型必须能够替换掉它们的父类型。(子类型一定要可以以父类型的身份出现)
M依赖倒转其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是针对抽象而不是针对细节编程,即程序中的所有依赖 关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计了。
--->N装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生产子类更为灵活。
—>代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问
应用场合有:
1)远处代理,也就是为一个对象在不同的地址空间提供局部代表;
2)虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象;
3)安全代理,用来控制真实对象访问时的权限;
4)职能指引,是指当调用真实的对象时,代理处理另外一些事;
—>P 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个,工厂方法使一个类的实例化延迟到其子类。
—>Q 原型模式(prototype):就是从一个对象再建一个可定制的对象,而且不需知道任何创建的细节。(clone())
—>R 模板方法模式:定义一个操作中的算法骨架,而将一些不走延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定不走。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
1)迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
—>外观模式(Facade):为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
—>建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1)使用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。
使用场合:建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
观察者模式:
2)定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。(Observer:抽象观察者,Subject:通知者)
3)将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
1)当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
2)一个抽象模型有两方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自的变化都不会影响另一边的变化。
菜鸟程序员碰到问题,只会用时间来摆平。
—>抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体的类。
1)IFactory是一个抽象工厂接口,它里面应该包括所有的产品创建的抽象方法。而ConcreteFactory1 \ConcreteFactory2就是具体的工厂了。
2)通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。
3)抽象工厂模式最大的好处便是易于交换产品系列,由于是具体的工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
编程是一门艺术,这样大批量的改动,显然是非常丑陋的做法。
4)应用反射+抽象工厂模式来解决数据库访问时的可维护、可扩展的问题。
5)所有在用简单工厂模式的地方,都可以考虑用反射技术来去除switch 或 if,解除分支判断带来的耦合。
switch ,if是程序里的好东西,但在应对变化上,却显得老态龙钟。反射技术的确可以很好地解决它们难以应付变化,难以维护和扩展的诟病。
一个程序员如果没有熬夜写程序的经历,不能算是一个好程序员,因为他没有痴迷过,所以他不会有大成就。
面向对象其实就是希望做到代码的责任分解。
状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像改变了其类。
--->状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个判断很简单,那就没有必要用“状态模式”了。
2)状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
3)将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。
4)使用状态模式是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,就像我们最早说的刻板印刷一样,任何变动和变化都是致命的。状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。
5)当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。P189
--->适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作。
1)系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配,适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。(主要是对象适配器模式 )
2)使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑使用适配器
--->备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
1)把要保存的细节给封装在了Memento中了,哪一天要更改保存的细节也不用影响客户端。
2)Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一部分时,Originator可以根据保存的Memento信息还原到前一状态。
3)如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
4)当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
–>组合模式(Composite):将对象组合成树形结构以表示“部分——整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
1)组合模式定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户端代码中,任何用到基本对象的地方度可以使用组合对象了。
2)用户不用关心到底是处理一个叶子还是处理一个组合组件,也就是用不着为定义组合而写一些选择判断语句。
3)组合模式让客户可以一致地使用组合结构和单个对象。
比如 :总公司,总公司财务部,总公司人力资源部;华东分公司,财务部,人力资源部,南京办事处(财务部,人力资源部)
–>单例模式:Singleton
保证一个类仅有一个实例,并提供一个访问它的全局访问点!
1)通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
具体做法是:把构造方法声明为private类型,只提供一个public类型getInstance()方法,判断是否为唯一的实例,让该类自己负责生成!这是懒汉式单例.
2)在静态初始化的方式是在自己被加载是就将自己实例化,所以被形象地称之为饿汉式单例类!
---> 桥接模式:(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化。
合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承!
1)聚合(Aggregation):表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;比如,大雁和雁群。
2)合成(组合 composition):则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样!比如,大雁和翅膀就是部分和整体的关系,它们有相同的生命周期。
3)合成/聚合复用原则的好处是,优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物!
4)你要学会用对象的职责,而不是结构来考虑问题。继承关系是一种强耦合的结构,父类变,子类就必须要变。
---》命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
1) 命令模式能比较容易地设计一个命令队列;
2)在需要的情况下,可以较容易地将命令记入日志;
3)允许接受请求的一方决定是否要否决请求。
4)可以容易地实现对请求的撤销和重做;
5)由于加入新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。
敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
---》指责链模式(Chain of Responsiblility):使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
1)接受者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构,结果是职责链可简化对象的相互连接,他们仅保持一个指向其后继者的引用,而不需保持它所有的候选接收者的引用,则会大大降低了耦合度。
2)我感觉由于是在客户端来定义链的结构,我可以随时增加或者修改处理一个请求的结构,增强了给对象指派指责的灵活性。
3)这的确很灵活,不过要当心,一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理,这就很糟糕了。
案例:小菜请求加薪1000,经理无权作主,向人力资源总监请示,总监也无权作主,只能想总经理汇报,总经理开始不同意(小于500才行),最后经过经理的建议,同意了!
---->中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使得各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
1)通过中介者对象,可以将系统的网状结构变成以中介者为中心的星形结构,每个具体对象不再通过直接的联系与另一个对象发生相互作用,而是通过"中介着“对象与另一个对象发生相互作用。中介者对象的设计,使得系统的结构不会因为新对象的引入造成大量的修改工作。
2)迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的话,可以通过第三方(也就是中介者)来发生关系,而不必直接通信。
3)中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式,而是要先反思你的系统在设计上是不是合理。
4)由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各本身的行为转移到他们之间的交互上来,也就是站在一个更宏观的角度去看待系统。
5)由于ConcretetMediator控制了集中化,于是就把交互复杂性变成了中介者的复杂性,则这就使得中介者会变得比任何一个ConcreteColleague都复杂。
案例:联合国安全理事会处理美国和伊拉克问题。
---》享元模式:运用共享技术有效地支持大量细粒度的对象。
1)如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;、
2)如果对象的大多数状态可以是外部状态,如果删除对象的外部状态,那麽可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式;
---》解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
1)如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
2)而所谓的解释器模式,正则表达式就是它的一种应用,解释器为正则表达式定义一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。
3)通常当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式;
4)用了解释器模式,就意味着可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义绰想语法树中各个节点的类的实现大体类似,这些类度易于直接编写;
5)说白了,解释器模式就是就是将这样的一句话,转变成实际的命令程序执行而已。而不用解释起模式本来也可以分析,但通过继承抽象表达式的方式,由于依赖倒转原则,使得对文法的扩展和维护都带来了方便;
6)解释器模式也有不足的,解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理;
案例:音乐播放器,正则表达等
---》访问者模式:它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作;
1)访问者模式适用于数据结构相对稳定的系统;它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化;
2)访问者模式的目的是要把处理从数据结构分离出来,很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易;
3)访问者模式的有点就是增加新的操作很容易,因为增加信的操作就意味着增加一个信的访问者,访问者模式将有关的行为集中到一个访问者对象中。
4)GOF四人中的一个作者就说过:‘大多时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了!‘;事实上,我们很难找到数据结构不变化的情况,所以用访问者模式的机会也就不太多了。
- 职业生涯
- 如何提升你的能力?给年轻程序员的几条建议
- 那些年,那些事
- 阿里巴巴离职DBA 35岁总结的职业生涯
- 人生的四种选择
- 程序人生的四个象限和两条主线
- 几缕代码与闲思
- 张小龙-学习笔记
- Web前端
- 移动Web手册
- 精通CSS: 高级Web标准解决方案
- 悟透JavaScript
- 架构设计
- 大型网站技术架构
- 周爱民-大道至简
- RESTful Web Services Cookbook - 读书笔记
- 大话设计模式
- Unix编程艺术
- 把程序员修炼之道读薄
- 学习能力
- 奇特的一生:读书笔记
- zhh-看源码那些事
- 一个创业者怎么看待读书和写作
- 程序员修炼之道
- 2015/1/5 头脑风暴
- 书单计划
- 2014年我读过的那些书
- 我的后端开发书架2015
- 别人的书单
- 读书笔记
- 浪潮之巅
- 达内时期自己笔记整理
- Effective Java
- 打造facebook: 读书笔记
- 面试整理
- 阿里面试的一点感受
- 腾讯的三轮面试
- 三十之惑–面霸
- 前端面试问题汇总
- 八爪网络面试总结
- 2015面试总结总结
- 找工作流程梳理
- 最全前端面试问题及答案总结
- 前端开发面试题收集
- 百度web前端--2015一面
- 百度web前端--2015二面