前言
设计模式的目标:提升代码重用性、可读性、可扩展性、可靠性,还能降低系统中类与类之间的耦合度,使程序呈现高内聚低耦合的特性。程序员在开始工作和学习的初期,要优先学习一下设计模式,逐渐应用到工作当中。写出高质量代码。
设计模式的作用,主要有以下4点:
1.代码重用性
相同功能的代码不用多次编写。
2.可读性
设计模式使程序易读,编程规范性,方便其他程序员的阅读和理解。
3.可扩展性
设计模式能使编写的程序具有良好的可扩展性,满足系统设计的开闭原则。比如策略模式,就是将不同的算法封装在子类中,在需要添加新的算法时,只需添加新的子类,实现规定的接口,即可在不改变现有系统源码的情况下加入新的系统行为。
4.可靠性
设计原则
单一职责
对于类来说,一个类应该只负责一项职责。
依赖倒转
类的具体形式交由使用者来管理
接口隔离
接口拆分最简,类去实现
里氏替换
减少类的继承关系,可以抽象出基类去继承
开闭原则
对修改关闭,对扩展开放,扩展时候尽量不动原来的代码
迪米特法则
减少代码的耦合
合成复用原则
减少类的继承,找出类中复用的东西抽取出来使用
常用 设计模式
单例模式
1.饿汉式(静态常量)
因为是常量(不会变化)且类加载时候实例化,所以没有线程同步问题
缺点:不能懒加载,会可能造成内存浪费
public class Singleton1 {
//构造器私有化,外部不能new
private Singleton1(){ }
//本类内部创建对象实例
private final static Singleton1 instance = new Singleton1();
/*提供一个公有的静态方法,返回实例对象*/
public final static Singleton1 getInstance(){
return instance;
}
}
2.饿汉式(静态代码块)
public class Singleton1 {
static{
instance = new Singleton1();
}
private Singleton1(){ }
private static Singleton1 instance;
public final static Singleton1 getInstance(){
return instance;
}
}
3.懒汉式(线程不安全)
解决了懒加载问题
缺点:有线程安全问题
public class Singleton3 {
private static Singleton3 instance;
private Singleton3(){
}
public static Singleton3 getInstance(){
/*使用时才创建*/
if(instance == null){
instance = new Singleton3();
}
return instance;
}
}
4.懒汉式(线程安全,加同步锁)
解决了线程安全问题
缺点:效率低
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){
}
public static synchronized Singleton4 getInstance(){
/*使用时才创建*/
if(instance == null){
instance = new Singleton4();
}
return instance;
}
}
5.双重检查(推荐使用)
为什么要双重检查?
如果直接锁里面判断是否存在再创建,会造成等待锁释放,影响效率
public class Singleton5 {
//volatile 确保变量可见性
private static volatile Singleton5 instance;
private Singleton5(){
}
/*
* 提供一个静态公有方法,当使用该方法时,才去创建instance
* 解决线程安全问题,同时又解决了懒加载问题,保证了执行效率。
*
*/
public static Singleton5 getInstance(){
/*使用时才创建*/
if(instance == null){
//将没有持锁的其他线程隔离,保证只创建一次
synchronized (Singleton5.class){
if (instance == null){
instance = new Singleton5();
}
}
}
return instance;
}
}
6.枚举方式
推荐使用,而且解决各种问题
把枚举当成类使用
public static void main(String[] args) {
Singleton singleton1 = Singleton.SINGLETON;
singleton2.sayOK();
}
enum Singleton{
SINGLETON; //属性
public void sayOK() {
System.out.println("OK~");
}
}
工厂模式
简单工厂模式
if else 返回对应对象
缺点:违反开闭原则,如果类多的话,考虑抽象工厂模式
工厂方法模式
产品需要分类 例如: 都是北京披萨,但是有不同口味
//继承抽象类实现创建方法,根据传值返回对象
public class BJOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
抽象工厂模式
产品需要分类 例如: 都是北京披萨,但是有不同口味
每个产品都有自己的工厂
//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
//让下面的工厂子类来 具体实现
public Pizza createPizza(String orderType);
}
//这是工厂子类
public class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
原型模式
实现clone方法
浅拷贝
父类Object实现的是浅拷贝
深拷贝
1.重写clone给成员对象赋值
2.通过序列化实现
//序列化
bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
构建者模式
1、产品(Product): 最终构建出来的对象
2、构建者(Builder): 实现抽象的构建方法
3、指挥者(Director):调用构建者的构建方法生成产品
适配器模式
总得来讲,就是对出参,入参的兼容
适用场景:
1、已经存在的类的接口不符合我们的需求;
2、创建一个可以复用的类,使得该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
3、在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类。
类适配器(将要适配的对象当做类,继承)
public static void main(String[] args) {
System.out.println(" === 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
//被适配的类
public class Voltage220V {
//输出 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
//获取到 220V 电压
int srcV = output220V();
int dstV = srcV / 44 ; //转成 5v return dstV;
}
}
对象适配器(将要适配的对象当做对象,传入)
//220V的电压转化为5V的电压
public static void main(String[] args) {
System.out.println(" === 对象适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;
//通过构造器,传入一个 Voltage220V 实例
public VoltageAdapter(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
@Override
public int output5V() {
int dst = 0;
if(null != voltage220V) {
int src = voltage220V.output220V();
System.out.println("使用对象适配器,进行适配~~");
dst = src / 44;
System.out.println("适配完成,输出的电压为=" + dst);
}
return dst;
}
}
接口适配器(将要适配的对象当做接口,继承 )
较复杂,内部类去实现方法适配
桥接模式
抽取同类型对象的共性,区分出实现层,抽象层
总结: 共性的抽取,组合(小米+手机=小米手机)
//手机通过传入品牌去构造,品牌里面有同品牌手机共有方法
//实现层品牌,抽象层手机
public static void main(String[] args) {
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
}
public interface Brand {
void open();
void close();
void call();
}
public class Vivo implements Brand {
@Override
public void open() {
System.out.println(" Vivo 手机开机 ");
}
@Override
public void close() {
System.out.println(" Vivo 手机关机 ");
}
@Override
public void call() {
System.out.println(" Vivo 手机打电话 ");
}
}
public abstract class Phone {
private Brand brand;
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
public class FoldedPhone extends Phone {
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 折叠样式手机 ");
}
public void close() {
super.close();
System.out.println(" 折叠样式手机 ");
}
public void call() {
super.call();
System.out.println(" 折叠样式手机 ");
}
}
装饰者模式
//1.抽象饮品 价格,描述属性;cost方法
//2.实际饮品继承抽象类,实现抽象cost方法
//3.装饰类继承抽象类,实现具体装饰(构造方法传入装饰类)
public static void main(String[] args){
Drink order = new Espresso();
order = new Milk(order);
order = new Chocolate(order);
}
public abstract class Drink {
public String des;
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price
}
public void setPrice(float price) {
this.price = price;
}
public abstract float cost();
}
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
//单独抽一层通用装饰类实现子类装饰类的公用方法
public class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //组合
this.obj = obj;
}
@Override
public float cost() {
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
return des + " " + getPrice() + " && " + obj.getDes();
}
}
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
setDes(" 牛 奶 ");
setPrice(2.0f);
}
}
组合模式
public static void main(String[] args) {
//从大到小创建对象 学校
OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
//创建 学院
OrganizationComponent computerCollege = new College(" 计 算 机 学 院 ", " 计 算 机 学 院 ");
OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
//创建各个学院下面的系(专业)
computerCollege.add(new Department("软件工程", " 软件工程不错 ")); computerCollege.add(new Department("网络工程", " 网络工程不错 "));
computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 ")); infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
//将学院加入到 学校
university.add(computerCollege); university.add(infoEngineercollege);
//university.print();
infoEngineercollege.print();
}
public abstract class OrganizationComponent {
private String name; // 名 字
private String des; // 说 明
protected void add(OrganizationComponent organizationComponent) {
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent) {
throw new UnsupportedOperationException();
}
public OrganizationComponent(String name, String des) {
super();
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
protected abstract void print();
}
public class College extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
public College(String name, String des) { super(name, des);
}
protected void add(OrganizationComponent organizationComponent) {
}
organizationComponents.add(organizationComponent);
}
protected void remove(OrganizationComponent organizationComponent) {
}
@Override
public String getName() {
}
@Override
public String getDes() {
// TODO Auto-generated method stub return super.getDes();
}
@Override
protected void print() {
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
public class University extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
public University(String name, String des) {
super(name, des);
}
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println("--------------" + getName() + "--------------");
for (OrganizationComponent organizationComponent:organizationComponents) {
organizationComponent.print();
}
}
外观模式
为系统中的一组接口,提供一个一致的界面
public static void main(String[] args) {
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
homeTheaterFacade.ready();
homeTheaterFacade.play();
homeTheaterFacade.end();
}
public class HomeTheaterFacade {
private TheaterLight theaterLight;
private Popcorn popcorn;
private Stereo stereo;
private Projector projector;
private Screen screen;
private DVDPlayer dVDPlayer;
//构造器
public HomeTheaterFacade() {
super();
this.theaterLight = TheaterLight.getInstance();
this.popcorn = Popcorn.getInstance();
this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance();
this.dVDPlayer = DVDPlayer.getInstanc();
}
//操作分成 4 步
public void ready() {
popcorn.on();
popcorn.pop();
screen.down();
projector.on();
stereo.on();
dVDPlayer.on();
theaterLight.dim();
}
public void play() {
dVDPlayer.play();
}
public void pause() {
dVDPlayer.pause();
}
public void end() {
popcorn.off();
theaterLight.bright();
screen.up();
projector.off();
stereo.off();
dVDPlayer.off();
}
}
public class DVDPlayer {
private static DVDPlayer instance = new DVDPlayer();
public static DVDPlayer getInstanc() {
return instance;
}
public void on() {
System.out.println(" dvd on ");
}
public void off() {
System.out.println(" dvd off ");
}
public void play() {
System.out.println(" dvd is playing ");
}
public void pause() {
System.out.println(" dvd pause ..");
}
}
public class Popcorn {
private static Popcorn instance = new Popcorn();
public static Popcorn getInstance() {
return instance;
}
public void on() {
System.out.println(" popcorn on ");
}
public void off() {
System.out.println(" popcorn ff ");
}
public void pop() {
System.out.println(" popcorn is poping ");
}
}
等等很多设备......
享元模式
各种池技术
1)享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象\
2)常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个\
3)享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率\
4)享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。***
//抽象网站
public abstract class WebSite {
public abstract void use(User user);//抽象方法
}
//具体网站
public class ConcreteWebSite extends WebSite {
//共享的部分,内部状态
private String type = ""; //网站发布的形式(类型)
//构造器
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
// TODO Auto-generated method stub
System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
}
}
// 网站工厂类,根据需要返回压一个网站
public class WebSiteFactory {
//集合, 充当池的作用
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
public WebSite getWebSiteCategory(String type) {
if(!pool.containsKey(type)) {
//就创建一个网站,并放入到池中
pool.put(type, new ConcreteWebSite(type));
}
return (WebSite)pool.get(type);
}
//获取网站分类的总数 (池中有多少个网站类型) public int getWebSiteCount() {
return pool.size();
}
}
代理模式
静态代理
//代理对象实现和被代理对象一样的接口方法
//1)优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
//2)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
//3)一旦接口增加方法,目标对象与代理对象都要维护
public interface ITeacherDao {
void teach(); // 授课的方法
}
public class TeacherDao implements ITeacherDao {
public void teach() {
System.out.println(" 老师授课中 。。。。。");
}
}
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target) { \
this.target = target;
}
@Override
public void teach() {
System.out.println("开始代理完成某些操作。。。。。 ");
target.teach();
System.out.println("提交。。。。。");
}
}
动态代理
JDK代理
1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
2)代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
3)动态代理也叫做:JDK 代理、接口代理
spring也是按照以下规则选择:
在代理对象不是借口类型或不是代理类时,指定proxyTargetClass=true后,执行CGLIB代理
代理对象是接口类型或是代理类,使用JDK代理
public static void main(String[] args) {
//创建目标对象
ITeacherDao target = new TeacherDao();
//给目标对象,创建代理对象, 可以转成 ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println("proxyInstance=" + proxyInstance.getClass());
//通过代理对象,调用目标对象的方法
//proxyInstance.teach ();
proxyInstance.sayHello(" tom ");
}
public class ProxyFactory {
//维护一个目标对象 ,object
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
}
//给目标对象 生成一个代理对象
public Object getProxyInstance() {
//说明
/*
*
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行
的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK 代理开始~~");
//反射机制调用目标对象的方法
Object returnVal = method.invoke(target, args);
System.out.println("JDK 代理提交"); return returnVal;
}
});
}
Cglib代理(子类代理)
不支持final/static方法
1)静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 Cglib 代理
2)Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理。
3)Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
4)在 AOP 编程中如何选择代理模式:
1.目标对象需要实现接口,用 JDK 代理
2.目标对象不需要实现接口,用 Cglib 代理
5)Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
public static void main(String[] args) {
//创建目标对象
TeacherDao target = new TeacherDao();
//获取到代理对象,并且将目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发 intecept 方法,从而实现 对目标对象的调用
String res = proxyInstance.teach();
System.out.println("res=" + res);
}
public class ProxyFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
//构造器,传入一个被代理的对象
public ProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象: 是 target 对象的代理对象
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
//重写 intercept 方法,会调用目标对象的方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println("Cglib 代理模式 ~~ 开始");
Object returnVal = method.invoke(target, args);
System.out.println("Cglib 代理模式 ~~ 提交");
return returnVal;
}
}
命令模式
优点
1)将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
2)容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
3)容易实现对请求的撤销和重做
4)命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
5)空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
代码
//扩展的时候,增加一个对应的命令还有接收者
public static void main(String[] args) {
//使用命令设计模式,完成通过遥控器,对电灯的操作
//创建电灯的对象(接受者)
LightReceiver lightReceiver = new LightReceiver();
//创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
//需要一个遥控器
RemoteController remoteController = new RemoteController();
//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("--------按下灯的开按钮-----------");
remoteController.onButtonWasPushed(0);
System.out.println("--------按下灯的关按钮-----------");
remoteController.offButtonWasPushed(0);
System.out.println("--------按下撤销按钮-----------"); remoteController.undoButtonWasPushed();
}
}
//命令接口
public interface Command {
//执行动作(操作)
public void execute();
//撤销动作(操作)
public void undo();
}
//具体命令
public class LightOffCommand implements Command {
// 聚 合
LightReceiver LightReceiver light;
// 构造器
public LightOffCommand(LightReceiver light) {
super();
this.light = light;
}
@Override
public void execute() {
// 调用接收者的方法
light.off();
}
@Override
public void undo() {
// 调用接收者的方法
light.on();
}
}
//接收者
public class LightReceiver {
public void on() {
System.out.println(" 电灯打开了.. ");
}
public void off() {
System.out.println(" 电灯关闭了.. ");
}
}
//调用者
public class RemoteController {
// 开 按钮的命令数组
Command[] onCommands; Command[] offCommands;
// 执行撤销的命令
Command undoCommand;
// 构造器,完成对按钮初始化
public RemoteController() {
onCommands = new Command[5]; offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand();
}
// 给我们的按钮设置你需要的命令
public void setCommand(int no, Command onCommand, Command offCommand) { onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开按钮
public void onButtonWasPushed(int no) { // no 0
// 找到你按下的开的按钮, 并调用对应方法
onCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = onCommands[no];
}
// 按下开按钮
public void offButtonWasPushed(int no) {
// 找到你按下的关的按钮, 并调用对应方法
offCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButtonWasPushed() { undoCommand.undo();
}
}
访问者模式
优点
优点
1)访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
1)具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
2)违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
3)因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.
代码
public abstract class Action {
//得到男性 的测评
public abstract void getManResult(Man man);
//得到女的 测评
public abstract void getWomanResult(Woman woman);
}
public class Fail extends Action {
@Override
public void getManResult(Man man) {
System.out.println(" 男人给的评价该歌手失败 !");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(" 女人给的评价该歌手失败 !");
}
}
public abstract class Person {
public abstract void accept(Action action);
}
public class Man extends Person {
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
//数据结构,管理很多人(Man , Woman)
public class ObjectStructure {
//维护了一个集合
private List<Person> persons = new LinkedList<>();
//增加到 list
public void attach(Person p) {
persons.add(p);
}
//移除
public void detach(Person p) {
persons.remove(p);
}
//显示测评情况
public void display(Action action) {
for(Person p: persons) {
p.accept(action);
}
}
}
//调用
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
//成功
Success success = new Success();
objectStructure.display(success);
Fail fail = new Fail();
objectStructure.display(fail);
Wait wait = new Wait();
objectStructure.display(wait);
迭代器模式
监听者模式
//1.把监听器都放到一个数组里面 2.传一个事件进去 3.遍历监听器执行
public class EventSource {
//监听器列表,如果监听事件源的事件,注册监听器可以加入此列表
private Vector<MonitorListener> listenerList = new Vector<>();
//注册监听器
public void addListener(MonitorListener eventListener) {
listenerList.add(eventListener);
}
//删除监听器
public void removeListener(MonitorListener eventListener) {
int i = listenerList.indexOf(eventListener);
if(i >= 0) {
listenerList.remove(eventListener);
}
}
//接受外部事件,通知所有的监听器
public void notifyListenerEvents(PrintEvent event) {
// Iterator<MonitorListener> iterator = listenerList.iterator();
// while(iterator.hasNext()) {
// MonitorListener monitorListener = (MonitorListener)iterator.next();
// monitorListener.handleEvent(event);
// }
for(MonitorListener moniterListener : listenerList) {
moniterListener.handleEvent(event);
}
}