UML图:
------> 依赖
——>关联
-------▲ 实现
—–—▲ 继承
🔺———> 聚合
▲———> 组合(关联性更强)
策略模式:是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法和行为。
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式Context对象
对于一个类来说,应该仅有一个引起它变化的原因。
将一个应用的功能剥离开(例如:视图和业务逻辑),否则将这些职责耦合在一起,会导致这个应用很脆弱。耦合性高,代码复用性低。
比如:俄罗斯方块游戏:可以分为界面窗体类+游戏逻辑类(旋转、移动、下落、堆积)
对于软件实体来说,应该可以对外扩展,但是不可修改。
在我们最初编写代码时,假设变化不会发生。
当变化发生时,我们就创建抽象(接口、抽象类)来隔离发生的同类变化。
对于一个系统来说,每个部分的功能应该独立且单一,当出问题了只需要修改相应的部分即可(单一职责原则),并且应该尽量做到对外扩展开发,对外修改关闭。(开闭原则)。每个部分之间可以做到轻松的组合起来(依赖倒转原则,面向抽象编程(接口))
在应用中
不应该高层模块依赖底层模块
,容易出现底层模块发生变化,高层也要变化。所以,无论是高层还是底层都应该依赖于接口,这样如果接口稳定,就不用担心其他模块受到影响。
一个软件实体如果使用的是一个父类的话,那么一定是用于它的子类,而且察觉不出来区别。
任何父类出现的地方都可以用子类代替。
由于子类型的可替换性,才使得使用夫类型的模块在无需修改的情况下可以扩展。
装饰器模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
代码实现:
Component:
abstract class Component {
public abstract void Operation();
}
ConcreteComponent(真实对象)
class concreteComponent extends Component {
public override void Operation (){
Console.WriteLine("具体的对象操作");
}
}
Decorator(对原对象进行扩展)
abstract class Decorator extends Component {
protected Compoment compoment;
public void setCompoment(Compoment compoment) {
this.compoment = compoment;
}
@Override
public void Operation() {
if(compoment != null) {
compoment.Operation();
}
}
}
ConcreteDecoratorA(具体的装饰对象)
public class ConcreteDecoratorA extends Decorator{
private String strA = "A的功能扩展";
@Override
public void Operation() {
compoment.Operation();
strA = "A";
System.out.println("装饰者A的装饰");
}
}
ConcreteDecoratorB(具体的装饰对象)
public class ConcreteDecoratorB extends Decorator{
@Override
public void Operation() {
compoment.Operation();
actionByB();
System.out.println("装饰者B的装饰");
}
public void actionByB() {
System.out.println("装饰者B特有的行为");
}
}
客户端代码:
public class Main {
public static void main(String[] args) {
Compoment p = new ConcreteComponent(); //创建一个具体对象
ConcreteDecoratorA cona = new ConcreteDecoratorA(); //装饰者A
cona.setCompoment(p); //设置装饰的对象
ConcreteDecoratorB conb = new ConcreteDecoratorB(); //装饰者B
conb.setCompoment(cona); //设置装饰的对象
conb.Operation();
}
}
小节:
装饰模式是为已有的功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象。因此,客户代码就可以在运行时根据需要有选择地、按顺序的使用装饰功能包装对象。
为其他对象提供一种代理以
控制
对这个对象的访问。一般使用的场合:远程代理(反向代理)、虚拟代理(存放实例化需要很长时间的真实对象)、安全代理(用来控制真实对象访问时的权限)。
代码实现:
公用接口
abstract class Subject {
public astract void Request();
}
真实实体类:
class RealSubject extends Subject {
public override void Request() {
Console.WriteLine("真实的请求");
}
}
代理类:
class Proxy extends Subject {
RealSubject realSubject;
public override void Request() {
if(realSubject == null) {
realSubject = new RealSubject();
}
realSubject.Request();
}
}
客户端代码:
public static void main (String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
Console.Read();
}
装饰器模式和代理模式的区别:
- 装饰器模式强调的时增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。而代理模式强调要让别人帮你去做一些本身于你业务没有太多的关系的职责(日志处理、设置缓存),将核心业务和边缘业务分离开,降低程序的耦合度。
- 装饰模式是以对客户端透明的方式扩展对象的功能;代理模式则是给一个对象提供一个代理对象,由代理对象来
控制
对象对原有对象的使用。- 装饰器模式的功能由客户端根据需求进行选择,更灵活,而代理模式的功能已经写死了
- 代理注重对原对象的控制(代理模式中目标对象的生命周期是代理对象控制的),不易于二次扩展。装饰器通过上层装饰,易于扩展。
//1、简单工厂的结构、缺点
如果我们要设计一个代码复用性高、扩展性高(开闭原则),并且基于依赖反转以及单一职责原则我们可以实现一个基类(运算抽象类),然后分别具体的加法类、减法类等重写运算类。最后实现一个简单工厂类,可以根据不同的规则创建不同的对象。
但是,当我们要新增一个开根运算运算时,我们需要继承运算类,然后重写方法,最后修改工厂类逻辑,此处破坏了开闭原则,导致程序的耦合度过高。所以我们引入了工厂方法模式
简单工厂类结构图
//2、简单工厂模式和策略模式的区别
- 用途不一样,简单工厂类是创建型模式,它的作用是创建对象。策略模式是行为型模式,作用是在许多行为中选择一种行为,关注的是行为的多样性。
- 解决的问题不同。简单工厂模式是解决资源的统一分发,将对象的创建分离开。策略模式解决的是策略的切换和扩展。
==重点:==如果在适用策略模式的地方使用简单工厂模式,如果新增加策略就要修改工厂类,而这个可能会导致其他错误,也破坏了开闭原则。如果使用了策略模式,只要将新增加的策略当作参数传递到Context类中即可。
//3、工厂方法模式的结构、优点
于是工厂方法模式来了,直接看结构图:
工厂方法模式结构图
根据上图可以看出,工厂方法模式多了具体运算类的工厂类,所以当我们要新增运算,我们的步骤是:
- 编写具体类
实现
运算类重写运算方法.- 实现自己的工厂类
实现
抽象工厂类。- 根据需要,在客户端选择合适的工厂创建不同的运算对象。
所以工厂方法模式解决了简单工厂破坏了开闭原则的缺点。
用原型实例指定创建对象的种类,并通过这些原型实例创建新的对象。
一句话就是通过clone()克隆在特定场景下代替new创建对象。
值得注意的是有深拷贝和浅拷贝。
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义改算法的某些步骤。
当子类出现相同的逻辑,我们可以将这些不变的逻辑
提取放在超类中去实现。通过父类中定义抽象方法,在不同子类之间实现这个方法来实现可变的逻辑。
也叫最少知识原则,如果两个类不必直接通信,那这两个类不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
每一个类都应当尽量降低成员的访问权限,迪米特法则强调类之间的松耦合,类之间的耦合越弱越容易被复用。
为子系统的一组接口(股票)提供一个一致的界面(基金),此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
也就是定义一个中间类(外观)
,对所要使用的类进行封装,之后可以直接调用这个中间类(外观)
就行了。
外观模式体现了依赖倒转、迪米特法则
结构图:
何时使用外观模式:
- 首先,设计初期,有意识地将不同的两个层分离,层与层之间建立外观类
- 其次,在开发阶段,子系统可能越来越复杂,增加外观类,减少他们的依赖。
- 第三、维护遗留的大型系统时(很难维护、扩展),可以新增外观类对老系统进行屏蔽,之后新系统可以基于外观类进行开发。
//1、建造者模式是什么?
建造者模式:将一个复杂对象的构建(人的构建:头、胳膊、腿)和它的表示分离,使得同样的构建过程可以创建不同的表示。
//2、为什么要有建造者模式
用了建造者模式后,用户只需指定需要建造的类型就可以得到它们,而具体的建造过程和细节就不需要知道。
//3、建造者模式的结构
//4、什么时候需要使用建造者模式呢?
主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
//1、特点
又叫发布-订阅模式。定义了一种一对多的依赖关系(同事依赖前台小姐姐盯住老板),让多个观察者对象同时监听某一个主题对象。这个主题对象状态变化时,会通知所有观察者模对象,是他们能够自动更新自己。
//2、结构
//3、使用
应用场景:当一个对象的改变需要同时改变其他对象的时候,而且不需要知道有多少对象待改变时。
观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响另一边的变化。
但是:抽象通知者还是依赖抽象观察类,并且不同的观察者针对通知者的变化可能有不同的行为,所以可以去掉抽象观察类,使用委托方法来优化。
- 委托可以搭载多个方法,我们可以像调用方法一样调用委托,所有的方法都会被唤起,这些方法不需要属于同一个类。但是所搭载的方法必须具有相同的原型(形参、返回值)
抽象工厂模式和工厂方法模式很类似,提供一系列
相关或相互依赖对象的接口,而无需指定他们具体的类。
具体的工厂类中有多个产品的createxxx()方法(Iuser、Idepart)。
优缺点:
优点:
- 将具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离。
缺点:
- 当需要添加新需求的时候(product),需要新增三个类(IProduct、具体的实现),以及修改工厂类。
改进:
去除工厂类,新增DataAccess类,根据String db = ‘’xxx‘’,通过case “不同数据库” 创建不同的实例。
但是我们可以使用反射去优化,并通过配置文件,将使用的具体的数据库赋值给db。
当控制一个对象状态装换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类中。通过把各种状态转换逻辑分布到State的子类之间,来减少相互之间的依赖。
何时使用状态模式:
当一个对象的行为取决于他的状态,并且他必须在运行时刻根据状态改变它的行为时。
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
分为类适配器
、对象适配器
类适配器UML结构图:
通过继承原有类、实现目标接口来实现适配。
对象适配器UML结构图:
继承原有类、关联目标对象(类似代理)。