分类
创建者模式
结构型模式
行为型模式
显示了模型的静态结构
Demo |
---|
+ age:int |
+ method(args 1):int |
+:public
-:private
#:protected
属性完整表示: 可见性 名称:类型
方法完整表示: 可见性 名称(参数列表)[:返回类型]
1.单向关联
一个顾客有一个地址
2.双向关联
一个顾客可以有多个商品,一件商品也有一个顾客
3.自关联
自己包含自己
强关联关系,是整体和部分之间的关系
组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。
在整合关系中,整体对象可以控制部分对象的生命周期,一旦整体不存在,部分也不将存在。
依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
继承关系是对象之间耦合度最大的一种关系,表示一般和特殊的关系,是父类与子类之间的关系,是一种继承关系。
实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所有声明的抽象方法。
对扩展开放,对修改关闭。在程序需要进行拓展时,不能修改原有代码。想达到这种效果,需要使用抽象类和接口。
任何基类出现的地方,子类一定可以出现。
子类可以拓展父类的功能,但不能改变父类的功能。
换句话说,子类继承父类时,除了添加新功能外,不要重写父类的方法。
正方形继承长方形,重写了setLength方法,违反里氏代换原则。
高层模块不应该依赖底层模块,两者应该依赖其抽象;抽象不应该依赖细节,细节应该抽象。简单的说,就是要对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块之间的耦合。
比如组装电脑。只需要,内存,机箱cpu,硬盘,只要这些东西有了,就能运行计算机。但具体的品种可以选择。
客户端不应该被迫依赖于它不适用的方法。一个类对另一个类的依赖应该建立在最小的接口上。
又叫最少知识原则。
只和你的直接朋友交谈,不跟“陌生人”说话。
其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的互相调用,可以通过第三方(包含了这两个软件)转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象,当前对象创建的对象、当前对象的方法参数等。这些对象同当前对象存在关联、聚合或者组合关系,可以直接访问对象的方法。
尽量使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
采用组合或者聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能。
创建者模式的主要关注点是“怎样创建对象”。它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不用关注对象的创建细节。
创建者模式分为:
单例模式是java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了创建对象的最佳方式。
饿汉:类加载就导致该单实例被创建
懒汉:首次需要使用时,才会创建对象
饿汉式-静态方式
public class Singleton{
//1.私有构造方法
private Singleton(){
}
//2.在本类中创建本类对象
private static Singleton instance = new Singleton();
//3.提供一个公共的访问方式,让外界获取对象
public static Singleton getInstance(){
return instance;
}
}
饿汉式-静态代码块
public class Singleton{
//1.私有构造方法
private Singleton(){
}
//2.声明本类对象
private static Singleton instance = null;
//3.在代码块中进行赋值
static{
instance = new Singleton();
}
//4.提供一个公共的访问方式,让外界获取对象
public static Singleton getInstance(){
return instance;
}
}
饿汉式-枚举类
public enum Singleton{
INSTANCE;
}
懒汉式-线程不安全
public class Singleton{
//1.私有构造方法
private Singleton(){}
//2.声明本类对象
private static Singleton instance = null;
//3.提供一个公共的访问方式,让外界获取对象
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
懒汉式-线程安全
public class Singleton{
//1.私有构造方法
private Singleton(){}
//2.声明本类对象
private static Singleton instance = null;
//3.提供一个公共的访问方式,让外界获取对象
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
懒汉式-双锁检查
因为锁降低了性能
public class Singleton{
//1.私有构造方法
private Singleton(){}
//2.声明本类对象
private static volatile Singleton instance = null;
//volatile保证指令可见性,有序性。去掉,在多线程时,可能出现空指针问题
//3.提供一个公共的访问方式,让外界获取对象
public static Singleton getInstance(){
//第一次判断,为空,抢占锁
if(instance == null){
sychronized(Singleton.class){
//第二次判断,为空,创建对象
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
懒汉式-静态内部类 不加锁且安全
JVM在加载外部类时,是不会加载静态内部类的。只有内部类的属性/方法被调用时,才会被加载。
public class Singleton{
//1.私有构造方法
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并名初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//3.提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
序列化和反序列化破坏单例模式
//将对象写入文件,再将对象读出文件
反射破坏单例模式
Class clazz = Singleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Singleton s1 = (Singleton) cons.newInstance();
Singleton s2 = (Singleton) cons.newInstance();
//实现Serializable接口,写readResolve()方法
public class Singleton implements Serializable{
//1.私有构造方法
private Singleton(){}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并名初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//3.提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
//当进行反序列化时,会自动调用该方法。
public Object readResolve(){
return SingletonHolder.INSTANCE;
}
}
public class Singleton{
private static boolean flag = flase;
//1.私有构造方法
private Singleton(){
sychronized(Singleton.class){
//判断是否是第一次访问
if(flag){
throw new RuntimeException("不能创建多个对象")
}
}
}
//2.定义一个静态内部类
private static class SingletonHolder{
//在内部类中声明并名初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//3.提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
简单工厂不是一种设计模式,反而比较像一种编程习惯。
提供方法给用户来获取对象。
优点:
解耦,但增加了产品和工厂的耦合。
缺点
增加新产品时,需要修改工厂类中的代码。违背了开闭原则。
结构:
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类
结构
实现
工厂:
public interface CoffeFactory{
Coffee createCoffee();
}
public class AmericanCoffeeFactory implements CoffeeFactory{
public Coffee createCoffee(){
return new AmericanCoffee();
}
}
商店:
public class CoffeeStore{
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory){
this.factory = factory;
}
//点咖啡
public Coffee orderCoffee(){
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
测试:
psvm{
//创建咖啡店
CoffeeStore store = new CoffeeStore();
//创建咖啡工厂
CoffeeFactory factory = new AmericanCoffeeFactory();
store.setFactory(factory);
//点咖啡
Coffee coffee = store.orderCoffee();
}
优点
缺点
工厂方法模式只考虑生产同级别的产品,但是现实生活中,许多工厂时综合性的工厂,能生产多种产品。
抽象工厂模式,将考虑多等级产品的生产。
同一个产品族可以指的是,同一个厂家生产的。
构成
实现
优点
当一个产品族中的多个对象被设计成一起工作时,他能保证客户端始终只使用同一个产品族中的产品
缺点
当产品族需要增加一个新产品时,所有的工厂类都需要进行修改。
使用场景
描述如何将类或者对象按照某种布局组成更大的结构。它分为类结构型模式和对象结构型某事,前者采用继承机制来组织接口和类,后者采用组合或者聚合来组合对象。
由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象的中介。
结构
售票的是抽象主题类,火车站等是真实主题类,代理商是代理类。
public interface SellTickets{
void sell();
}
public TrainStation implements SellTickets{
public void sell(){
sout("火车站卖票");
}
}
//代理类
public class ProxyPoint implements SellTickets{
//声明火车站类对象
private TranStation trainStation = new TranStation();
public void sell(){
sout("代售点收一点代理费");
trainStation.sell();
}
}
java中提供一个动态代理类Proxy,提供一个创建代理对象的静态方法。(newProxyInstance方法)。
只对接口进行代理.
public interface SellTickets{
void sell();
}
public TrainStation implements SellTickets{
public void sell(){
sout("火车站卖票");
}
}
//动态代理
public class ProxyFactory{
//声明被代理对象
private TrainStation train = new TrainStation();
public SellTickets getProxyObject(){
SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args)throws Exception{
sout("invoke方法被调用了");
sout("收取一定费用---jdk");
Object obj = method.invoke(station,args); //执行被代理对象的方法。
return obj;
}
}
);
return proxyObject;
}
}
psvm{
proxyObject.sell(); //这是增强之后的对象.
}
底层原理
//需要导入cglib jar包
public interface SellTickets{
void sell();
}
public TrainStation implements SellTickets{
public void sell(){
sout("火车站卖票");
}
}
//cglib动态代理
public class ProxyFactory implements MethodInterceptor{
private TrainStation station = new TrainStation();
public TrainStation getProxyObject(){
//创建Enhancher对象,类似于JDK代理中的Proxy类
Enhancer enhancer = new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(TrainStation.class);
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation proxyObject = (TrainStation)enhancer.create(); //返回TrainStation的子类
return proxyObject
}
public Ojbect Entercept(Object o,Method method,Object[]objects,MethodProxy methodProxy) throws Exception{
sout("sell()方法执行了");
sout("增强");
Object obj = method.invoke(station,objects);
return obj;
}
}
psvm{
ProxyFactory factory = new ProxyFactory();
TraninStation t = factory.getProxyObject();
t.sell();
}
优点:
缺点:
定义
将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类一起工作.
结构
实现方式
定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
现有一台电脑只能读取SD卡,而要读取TF卡中内容,这就需要用到适配器。
适配者类:
public interface TFCard{
//往TF卡中读
String readTF();
//往TF卡中写
void writeTF(String msg);
}
public class TFCardImpl implements TFCard{
pubic String readTF(){
String msg = "读取TF卡";
return msg;
}
public void writeTF(String msg){
sout("写TF卡" + msg);
}
}
目标接口类:
public interface SDCard{
//往SD卡中读
String readSD();
//往SD卡中写
void writeSD(String msg);
}
public class SDCardImpl implements SDCard{
pubic String readSD(){
String msg = "读取TF卡";
return msg;
}
public void writeSD(String msg){
sout("写SD卡" + msg);
}
}
使用者:
public class Computer{
//从SD卡中读取数据
public String readSD(SDCard sdCard){
if(sdCard == null){
throw new NullPointerException("sd card is null");
}
return sdCard.readSD();
}
}
psvm{
//直接读SD卡
Computer c = new Computer();
String msg = c.readSD(new SDCardImpl());
//使用适配器读取SD卡
String msg = c.readSD(new SDAdapterTF());
}
适配器类:继承适配者类。实现目标接口
public class SDAdatpterTF extends TFCardImpl implements SDCard{
public String readSD(){
sout("适配器读取tf卡");
return readTF();
}
public void writerSD(String msg){
sout("适配器写tf卡");
writTF(msg);
}
}
缺点:违背了合成复用原则。类适配器是客户类有一个接口规范的情况下使用,反之不可用。(只能继承一个类)
实现
对象适配器模式可采用现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口.
适配器类:聚合(拥有)适配者类.
public class SDAdatpterTF implements SDCard{
//声明适配这类
private TFCard tfCard;
public SDAdatpterTF(TFCarc tfCard){
this.tfCard = tfCard;
}
public String readSD(){
sout("适配器读取tf卡");
return tfCard.readTF();
}
public void writerSD(String msg){
sout("适配器写tf卡");
tfCard.writTF(msg);
}
}
使用者类:
public class Computer{
//从SD卡中读取数据
public String readSD(SDCard sdCard){
if(sdCard == null){
throw new NullPointerException("sd card is null");
}
return sdCard.readSD();
}
}
psvm{
//直接读SD卡
Computer c = new Computer();
String msg = c.readSD(new SDCardImpl());
//使用适配器读取SD卡
//创建适配器类
SDAdapter sdAdapterTF = new SDAdapterTF(new TFCardImpl());
String msg = computer.readSD(sdAdapterTF);
}
快餐加料.若继承,或产生很多子类.
定义
在不改变现有对象结构的情况下,动态地给对象增加一些职责(即增加额外功能)地模式.
结构
抽象构件角色:快餐
public abstract class FastFood{
private float price;//价格
private String desc; //描述
//get and set方法
public FastFood(){
}
public FastFood(Float price, String desc){
this.price = price;
this.desc = desc;
}
public abstract float cost();
}
具体构件角色:
炒饭
public class FriedRice extends FastFood{
public FriedRice(){
super(10,"炒饭");
}
public float cost(){
return getPrice();
}
}
炒面
public class FriedNoodles extends FastFood{
public FriedRice(){
super(12,"炒面");
}
public float cost(){
return getPrice();
}
}
抽象装饰角色:(加了配料的快餐)
public abstract class Garnish extends FastFood{
//快餐
FastFood fastFood;
//get 和 set方法
public Garnish(FastFood fastFood,float price,String desc){
super(price,desc);
this.fastFood = fastFood;
}
}
具体装饰者类:
鸡蛋
public class Egg extends Garnish{
public Egg(FastFood fastFood){
super(fastFood,1,"加鸡蛋");
}
public float cost(){
return getPrice() + getFstFood().cost(); //快餐价格+鸡蛋价格
}
//描述
@Override
public String getDesc(){
return super.getDesc() + getFastFood().getDesc();
}
}
测试类
psvm{
//点一份炒饭
FastFood food = new FriedRice();
sout(food.getDesc() + " " + food.cost());
//加一个鸡蛋
food = new Egg(food);
sout(food.getDesc() + " " + food.cost());
}
好处
JDK中包装类BufferdInputStream,BufferedOutputStream,BufferedReader使用了装饰者模式.!
相同点:
不同点:
目的不同
装饰者是为了增强目标对象.
静态代理是为了保护和隐藏目标对象
获取目标对象的构建地方不同
装饰者是由外界传递进来,可以通过构造方法传递
静态代理是在代理类内部创建,以此来隐藏目标对象.
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足"合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
概述
在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。
例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。
定义:
定义一个操作中的算法骨架,而将算法中的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤.
结构
抽象类:负责给出一个算法的轮廓和骨架.它由一个模板方法和若干个基本方法构成
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法.
基本方法:是实现了算法各个步骤的方法,是模板方法的组成部分.基本方法又分为3中
一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
具体子类:实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
实现
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:
(抽象类,定义模板方法和基本方法)
public abstract class AbstractClass{
public final void cookProcess(){
this.pourOil();
this.heatOil();
this.pourVegetable();
this.pourSauce();
this.fry();
}
public void pourOil(){
sout("倒油");
}
public void heatOil(){
sout("热油");
}
//倒蔬菜和调料是不同的,因此用抽象方法
public abstract void pourVegetable(){
}
public abstract void pourSauce(){
}
pulibc void fry(){
sout("炒菜炒");
}
}
具体子类:
public class Caixin extends AbstractClass{
public void pourVegetable(){
sout("炒的是菜心");
}
public void pourSauce(){
sout("下的酱料是蒜蓉");
}
}
优点
将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中.
通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制,并符合"开闭原则"。
缺点
JDK源码中使用了模板方法
InputStream类中使用了模板方法.
概述
在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的"击鼓传花”游戏等。
定义
又名职责链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
结构
实现
现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
请假条类:
public class LeaveRequest{
private String name;
private int num;
private String content;
public LeaveRequest(String name, int num, String content){
this...;
}
//get 方法
}
抽象处理者类:
public abstract class Handler{
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
//领导处理的请求天数区间
private int numStart;
private int numEnd;
private Handler nextHandler;
public Handler(int numStart){
this...;
}
public Handler(int numStart, int numEnd){
this...;
}
//设置领导
public void SetNextHandler(Handler nextHnadler){
this.nextHandler = nextHandler;
}
//领导处理请假条的方法
protected abstract void handleLeave(LeaveRequest leaveRequest);
//提交上级领导
public final void submit(LeaveRequest leaveRequest){
this.handleLeave(leavRequest);
if(this.nextHandler != null && leaveRequest.getNum() > this.numEnd){
this.nextHandler.submit(leaveRequest);
}else{
sout("流程结束");
}
}
}
具体处理者类:
(小组长)
public class GroupLeader extends Handler{
public GroupLeader(){
super(0,Handler.NUM_ONE);
}
protected void HandleLeave(LeaveRequest leaveRequest){
sout(leaveRequest...);//请假条的信息
sout("小组长审批:同意");
}
}
(经理)
public class Manager extends Handler{
public GroupLeader(){
super(0,Handler.NUM_ONE);
}
protected void HandleLeave(LeaveRequest leaveRequest){
sout(leaveRequest...);//请假条的信息
sout("经理审批:同意");
}
}
客户端:
psvm{
LeaveRequest leave = new LeaveRequest("小明",1,"身体不适");
GroupLeader g = new GroupLeader();
Manager m = new Manager();
//设置处理者链
g.setNextHandler(m);
//小明提交请假条
g.submit(leave);
}
优点
降低了对象之间的耦合度
该模式降低了请求发送者和接收者的耦合度。
增强了系统的可扩展性
可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性
当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接
一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if或者 if…else语句。
责任分担
每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点
JAVAWEB
FilterChain是责任链模式的典型应用
定义
又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
结构
实现
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。
类图如下:
抽象主题:
public interface Subject{
//添加订阅者(添加观察者对象)
void attach(Observer observer);
//删除订阅者
voidetach(Observer observer);
//通知订阅者
void notify(String message);
}
具体主题
public class SubscriptionSubject implements Subject{
private List<Observer> weixinUserList = new ArrayList<Observer>();
public void attach(Observer observer){
weixinUserList.add(observer);
}
public void detach(Observer observer){
weixinUserList.remove(observer);
}
public void notify(String message){
//遍历结合
for(Observer o:weixinUserList){
observer.update(message);
}
}
}
抽象观察者类
public interface Observer{
void update(String message);
}
具体观察者类
public class WeiXinUser implements Observer{
private String name;
public WeiXinUser(String name){
this.name = name;
}
public void update(String message){
sout(name + "-" + message);
}
}
测试类
psvm{
//1.创建公众号对象
SubscriptionSubject s = new SubscriptionSubject();
//2.订阅公众号
subject.attach(new WeiXinUser("悟空"));
//3.公众号更新,发出信息给观察者
subject.notify("更新了");
}
优点
缺点:
JDK中通过java.util.Observable类和java.util.Observer接口定义了观察者模式