23种设计模式,一个没少。比较常用的:单例、工厂、适配器、装饰者、代理、观察者这几个模式。其他的做了解。
作用:确保一个类只有一个实例,只对外提供一个访问该实例的全局访问点。
特征:
1、无法通过new得到实例,构造器是被private修饰的
2、一般通过getInstance()的方法来获取它们的实例,而getInstance方法是对象的引用,并不是真正的new一个对象
对于单例模式,又分为这几类
饿汉式单例模式
我的理解就是饿汉就是初始化就对对象加载,没有延时
public class SingletonDemo1 {
//类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){
}
//方法没有同步,调用效率高!
public static SingletonDemo1 getInstance(){
return instance;
}
}
懒汉式加载(线程不安全)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式加载(线程安全)
public class SingletonDemo2 {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
private static SingletonDemo2 instance;
private SingletonDemo2(){ //私有化构造器
}
//方法同步,调用效率低!
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
双重检查锁
注意:
双重检测锁式:涉及到JVM底层内部模型,偶尔会出问题。不建议使用
public class SingletonDemo3 {
private volatile static SingletonDemo3 instance = null;
public static SingletonDemo3 getInstance() {
if (instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if (sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
private SingletonDemo3() {
}
}
静态内部类
这个方法需要熟悉使用,很多用到单例的地方用这个方法就差不多可以了,优点:线程安全,调用效率高,并且实现了延时加载!
public class SingletonDemo4 {
private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private SingletonDemo4(){
}
//方法没有同步,调用效率高!
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
枚举实现单例模式
这个方法也很好地避免了多线程同步的问题,并且还能够反序列化重新创建对象。
public enum SingletonDemo5 {
//这个枚举元素,本身就是单例对象!
INSTANCE;
}
实际上,这里的单例模式并不是真正的单例,当出现反射和反序列化的时候就会变成原来的普通对象
反射测试
//懒汉测试类
public class SingletonDemo06 /*implements Serializable*/{
private static SingletonDemo06 instace;
private SingletonDemo06() {//需要私有化构造器
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
}
通过反射调用私有构造器
public static void main(String[] args) throws Exception, SecurityException {
SingletonDemo06 s1 = SingletonDemo06.getInstace();
SingletonDemo06 s2 = SingletonDemo06.getInstace();
System.out.println(s1);
System.out.println(s2);
//通过反射的方式调用私有构造器
Class<SingletonDemo06> clz = (Class<SingletonDemo06>) SingletonDemo06.class;
Constructor<SingletonDemo06> cc = clz.getDeclaredConstructor();
cc.setAccessible(true);
SingletonDemo06 s3 = (SingletonDemo06)cc.newInstance();
SingletonDemo06 s4 = (SingletonDemo06)cc.newInstance();
System.out.println(s3);//Singleton.SingletonDemo06@2ff4acd0
System.out.println(s4);//Singleton.SingletonDemo06@54bedef2
}
很显然反射后的对象跟单例的hashcode不一样
解决反射的方式
在私有构造器加一层判断
public class SingletonDemo06 /*implements Serializable*/{
private static SingletonDemo06 instace;
private SingletonDemo06() {//需要私有化构造器
if(null != instace)
{
throw new RuntimeException();//防止反射下创建新的对象
}
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
}
反序列化Demo
public static void main(String[] args) throws Exception, SecurityException {
SingletonDemo06 s1 = SingletonDemo06.getInstace();
SingletonDemo06 s2 = SingletonDemo06.getInstace();
System.out.println(s1);//Singleton.SingletonDemo06@279f2327
System.out.println(s2);//Singleton.SingletonDemo06@279f2327
//通过反射的方式调用私有构造器
// Class clz = (Class) SingletonDemo06.class;
// Constructor cc = clz.getDeclaredConstructor();
// cc.setAccessible(true);
// SingletonDemo06 s3 = (SingletonDemo06)cc.newInstance();
// SingletonDemo06 s4 = (SingletonDemo06)cc.newInstance();
// System.out.println(s3);//Singleton.SingletonDemo06@2ff4acd0
// System.out.println(s4);//Singleton.SingletonDemo06@54bedef2
//通过反序列化的方式构造多个对象
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletonDemo06 s3 = (SingletonDemo06) ois.readObject();
System.out.println(s3);//Singleton.SingletonDemo06@26a1ab54
}
解决方法readResolve()直接返回指定的单例对象
public class SingletonDemo06 implements Serializable{
private static SingletonDemo06 instace;
private SingletonDemo06() {//需要私有化构造器
if(null != instace)
{
throw new RuntimeException();//防止反射下创建新的对象
}
}
//方法需要同步,调用效率低
public static synchronized SingletonDemo06 getInstace() {
if(null == instace)
{
instace = new SingletonDemo06();
}
return instace;
}
//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws Exception{
return instace;
}
}
总结:
单例模式的优点:
1、单例模式只产生一个实例,减少了性能的开销
2、当一个对象需要很多资源的时候,例如读取配置、产生依赖等,可以直接在启动时产生单例,让他永久驻留内存
3、单例可以让全局使用,达到优化资源共享
五种单例模式的实现方式比较:
单例模式的应用:
1、Windows的Task Manager(任务管理器)、回收站就是很典型的单例模式
2、 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作 ,否则内容不好追加。
3、spring容器的管理
主要是为了实现创建者和调用者的分离。通过工厂的方法代替new实例化对象,将实现类、创建对象统一管理和控制,达到调用者与实现者的解耦合。
简单工厂模式
一个抽象的接口,多个抽象接口的实现类,一个工厂类。弊端:一般是静态方法,对新增加的类必须修改代码。
public intereface Car{
void run();
}
public class Audi implements Car {
@Override
public void run() {
System.out.println("奥迪生产");
}
}
public class byd implements Car{
@Override
public void run() {
System.out.println("比亚迪生产");
}
}
//工厂类
public class CarFactory {
public static Car createCar(String type) {
if("奥迪".equals(type))
{
return new Audi();
}
else if ("比亚迪".equals(type)) {
return new byd();
}
else {
return null;
}
}
}
public static void main(String[] args) {
//没有工厂创建类
Car cc1 = new Audi();
Car cc2 = new byd();
cc1.run();
cc2.run();
//工厂下创建类
Car c1 = CarFactory.createCar("奥迪");
Car c2 = CarFactory.createCar("比亚迪");
c1.run();
c2.run();
}
工厂方法模式
四个角色:抽象工厂模式、具体工厂模式、抽象产品、具体产品。实现抽象工厂的子类实现实例化,工厂方法弥补了简单工厂模式的缺点,区别在于:多了接口的工厂类。弊端:工厂方法模式的工厂类随着产品类个数增加而增加,从而增加了结构的复杂程度。
Demo
接口
public interface Car {
void run();
}
public interface CarFactory {
Car createCar();
}
实体类
public class Audi implements Car {
@Override
public void run() {
System.out.println("奥迪00000");
}
}
public class AudiFactory implements CarFactory {
@Override
public Car createCar() {
return new Audi();
}
}
public class byd implements Car {
@Override
public void run() {
System.out.println("比亚迪byd");
}
}
public class BydFactory implements CarFactory {
@Override
public Car createCar() {
return new Byd();
}
}
public class Benz implements Car {
@Override
public void run() {
System.out.println("奔驰B!");
}
}
public class BenzFactory implements CarFactory {
@Override
public Car createCar() {
return new Benz();
}
}
但是工厂模式并没有简单工厂模式用的多。
抽象工厂模式
抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务 分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。区别在于:工厂模式只生产单一的产品,抽象工厂模式中的工厂生产多个产品
Demo
public interface CarFactory {
Engine createEngine();
Seat createSeat();
Tyre createTyre();
}
public interface Tyre {
void revolve();
}
public interface Seat {
void massage();
}
class LuxurySeat implements Seat {
@Override
public void massage() {
System.out.println("全自动!");
}
}
class LowSeat implements Seat {
@Override
public void massage() {
System.out.println("手动!");
}
}
class LuxuryTyre implements Tyre {
@Override
public void revolve() {
System.out.println("米其林轮胎!");
}
}
class LowTyre implements Tyre {
@Override
public void revolve() {
System.out.println("普通轮胎!");
}
}
工厂实例
public class LowCarFactory implements CarFactory {
@Override
public Seat createSeat() {
return new LowSeat();
}
@Override
public Tyre createTyre() {
return new LowTyre();
}
}
public class LuxuryCarFactory implements CarFactory {
@Override
public Seat createSeat() {
return new LuxurySeat();
}
@Override
public Tyre createTyre() {
return new LuxuryTyre();
}
}
总结:
1、 简单工厂模式:用的比较多,但在程序中并不是最好的
2、工厂方法模式 :不修改已有类的前提下,通过增加新的工厂类实现扩展。
3、抽象工厂模式 :可以对产品族扩展,但是不好增加产品
构建者模式
场景:如何构建一台手机?
因为会存在生产组件还有装配两个步骤才能生产出一台手机。
构建者模式就是适用于对象构建复杂的情况。通过构建和装配的解耦合。就可以有不同的装配,做出不同的对象。相同的装配也可能因为装配顺序导致对象不同。属于业务与算法的解耦。例StringBuilder的实现。
原型模式
类似于克隆,以一个对象为原型,复制出一个新的队象,并且具备原型的特点。直接克隆效率高。
实现:
1、Cloneable和clone方法
克隆存在的问题就是浅拷贝跟深拷贝。有学过指针的朋友就比较熟悉了。
浅拷贝问题:浅拷贝会导致两个对象都用同一块内存空间,导致数据公用
解决方法:深克隆,让已实现Clonable接口的类中的属性也实现Clonable接口
深克隆:让已实现Clonable接口的类中的属性也实现Clonable接口
Demo
浅拷贝
public class Sheep implements Cloneable,Serializable { //1997,英国的克隆羊,多利!
private String sname;
private Date birthday;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //直接调用object对象的clone()方法!
return obj;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Sheep(String sname, Date birthday) {
super();
this.sname = sname;
this.birthday = birthday;
}
public Sheep() {
}
}
深拷贝
public class Sheep2 implements Cloneable { //1997,英国的克隆羊,多利!
private String sname;
private Date birthday;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone(); //直接调用object对象的clone()方法!
//添加如下代码实现深复制(deep Clone)
Sheep2 s = (Sheep2) obj;
s.birthday = (Date) this.birthday.clone(); //把属性也进行克隆!
return obj;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Sheep2(String sname, Date birthday) {
super();
this.sname = sname;
this.birthday = birthday;
}
public Sheep2() {
}
}
总结:
1、如果需要创建大量对象,new会比原型模式低效
2、单例和原型是相反的,spring中创建bean就是这两种方式
适配器模式:是为了让原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
目标接口(target):使用者用到的接口。可以是具体类/抽象类/接口 。
被适配的类(Adaaptee):需要适配的类
适配器(Adapter):将被适配的类包装成目标接口
//需要被适配的类
public class Adaptee {
public void request()
{
System.out.println("客户请求");
}
}
//通过继承方式的适配器
public class Adapter extends Adaptee implements Target {
@Override
public void handleReq() {
// TODO 自动生成的方法存根
super.request();
}
}
//内部产生被适配对象
public class Adapter2 implements Target{
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
public void handleReq()
{
adaptee.request();
}
}
//目标接口
public interface Target {
void handleReq();
}
动态地给一个对象增加新的功能。装饰模式是一种用于代替继承的方式,不需要再通过继承就能扩展对象的新功能。
举个例子:我想要一台智能、既上天又可以下水还能陆地跑的车。但是我现在只有一台普通的车,我就要给不同的厂家改装这台车,有智能公司、飞机公司等等。最后得到我想要的车
Component抽象:实现客户端跟实例对象的交互,如 io流中的InputStream、OutputStream接口
ConcreteComponent:真实对象,如 io流中的FileInputStream、FileOutputStream,例子中手上的普通车
Decorator装饰角色:抽象装饰接口,方便对真实对象添加功能
ConcreteDecorator具体装饰角色: 真正的装饰,给真正对象包装一层外衣。如Io流中的BufferedOutputStream、BufferedInputStream
Demo
public interface Icar {
void move();
}
//ConcreteComponent 具体构件角色(真实对象)
class car implements Icar{
@Override
public void move() {
System.out.println("地上跑");
}
}
//Decorator 装饰
class SuperCar implements Icar{
protected Icar car;
public SuperCar(Icar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//具体的装饰角色
class Flycar extends SuperCar{
public Flycar(Icar car) {
super(car);
}
public void fly() {
System.out.println("天上飞");
}
@Override
public void move() {
super.move();
fly();
}
}
//具体的装饰角色
class Swimcar extends SuperCar{
public Swimcar(Icar car) {
super(car);
}
public void swim() {
System.out.println("水下游");
}
@Override
public void move() {
super.move();
swim();
}
}
//具体的装饰角色
class AIcar extends SuperCar{
public AIcar(Icar car) {
super(car);
}
public void autoMove() {
System.out.println("自动");
}
@Override
public void move() {
super.move();
autoMove();
}
}
public static void main(String[] args) {
car car = new car();
car.move();
System.out.println("增加新的功能,飞行----------");
//手动地给car添加功能
Flycar flycar = new Flycar(car);
flycar.move();
System.out.println("既可以飞 又可以水下游");
SuperCar car2 = new Flycar(car);
Swimcar swimcar = new Swimcar(car2);
swimcar.move();
SuperCar car3 = new Flycar(new Swimcar(new AIcar(new SuperCar(new car()))));
car3.move();//自动、水下游、天上飞
}
总结:
1、装饰模式(Decorator)也叫包装器模式
2、装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类
3、 扩展对象功能,比继承灵活,不会导致类个数急剧增加
4、能够多次装饰,对不同的业务可以有多种组合
5、可以根据需要扩展装饰
弊端:
1、装饰者模式易出错,不容易调试错误
2、产生小的对象可能会影响性能
将流程代码放到代理类中处理,通过代理控制对对象的访问。也是AOP(面向切面变成)的核心机制
举个例子:结婚可以选择专业的婚庆公司安排流程,原本没有婚庆公司新郎新娘就需要:准备工作->约酒店->节目安排->人员分工->结婚->其他事情。找了婚庆公司,婚庆公司就能完成准备工作->约酒店->节目安排->人员分工->其他事情。新郎新娘就只要完成结婚就行了。明星与经纪人也是,明星只要负责演出就行了,其余的琐事都是经纪人去完成
静态代理模式
Demo
public interface Star {
void sing();
}
public class RealStar implements Star {
@Override
public void sing() {
System.out.println("RealStar(明星本人).sing()");
}
}
public class ProxyStar implements Star {
private Star star;
public ProxyStar(Star star) {
super();
this.star = star;
}
@Override
public void sing() {
System.out.println("ProxyStar.bookTicket()");
System.out.println("ProxyStar.collectMoney()");
System.out.println("ProxyStar.confer()");
star.sing();
}
}
动态代理
通过一个处理器接口反射,动态代理生成类和对象
java.lang.reflect.InvocationHandler(处理器接口)
1、可以通过invoke方法实现对真实角色的代理访问。
2、每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
public class StarHandler implements InvocationHandler{
private Star realstar;
public StarHandler(Star realstar) {
super();
this.realstar = realstar;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
if(method.getName().equals("sing"))
{
object = method.invoke(realstar, null);
}
return object;
}
}
生成代理对象方式
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[] {Star.class}, handler);
桥接模式
用于处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立 的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
桥接模式:其实就是通过桥把两个变化维度连在一起,让每个维度可以随意变化
组合模式
将整个关系变成树形结构。
抽象构件(Component)角色: 定义了叶子和容器构件的共同点
叶子(Leaf)构件角色:无子节点
容器(Composite)构件角色: 有容器特征,可以包含子节点,其中某个子类可以对Composite下所有子类操作
外观模式
调用者减少跟每一个实体的使用
例如要注册某个公司,正常流程是:总经理去找工商局审核、去税务局登记、去银行开户等等
外观模式是实现了给总经理安排一个下手,下手去干这些事情,总经理只要一声令下
享元模式
享元模式是为了节省内存,将相同或相似的对象共享重用。
内部状态:可以共享,不会随环境变化而改变
外部状态:不可以共享,会随环境变化而改变
举个例子:
生产一种面包,它的大小颜色口味都是一样的,唯独最后打上的生产日期不一样。所以面包类应该有大小颜色口味等属性,唯独要专门弄一个生产日期的类给面包设定不一样的生产日期
总结:
1、极大减少内存中对象的数量,因为不同批次的面包会有很多个面包类,但是用addDate类就只要控制addDate类
2、 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
3、外部状态相对独立,不影响内部状态
观察者模式就像一个广播,通常是1:N的方式。当一个目标对象(Subject)发生改变时,需要告知一系列观察者对象。举个例子:一个电台主播临时决定今晚不开播,就需要告诉那些关注他的人今天有事没有开播。这种情况下目标对象就是这个主播,观察者就是这些观众。
主要实现方式就是把观察者放入容器中,保持观察者跟目标对象的一致性
//目标对象基本方法
public class Subject {
protected List<Observer> list = new ArrayList<Observer>();
public void registerObserver(Observer obs){
list.add(obs);
}
public void removeObserver(Observer obs){
list.add(obs);
}
//通知所有的观察者更新状态
public void notifyAllObservers(){
for (Observer obs : list) {
obs.update(this);
}
}
}
public class ConcreteSubject extends Subject {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
//主题对象(目标对象)值发生了变化,请通知所有的观察者
this.notifyAllObservers();
}
}
public interface Observer {
void update(Subject subject);
}
public class ObserverA implements Observer {
private int myState; //myState需要跟目标对象的state值保持一致!
@Override
public void update(Subject subject) {
myState = ((ConcreteSubject)subject).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
public static void main(String[] args) {
//目标对象
ConcreteSubject subject = new ConcreteSubject();
//创建多个观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将这三个观察者添加到subject对象的观察者队伍中
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.registerObserver(obs3);
//改变subject的状态
subject.setState(3000);
//我们看看,观察者的状态是不是也发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
应用:
Servlet中,监听器的实现
服务器对客户端的推送
责任链模式
将能够处理同一类请求的对象连成一条链,请求沿链传递,链上的对象能处理该请求就处理,不能就给下一个。举个例子:大学生请假,请假少于3天,辅导员就有权利批,大于3天小于7天,就得给院主任批,大于7天小于30天,就得给院长批,大于30天就让校长把你休学。
核心也是基于if else实现,不过不是if else 的嵌套,通过链表把这些负责人连接在一起。
Demo
请假信息封装
public class LeaveRequest {
private String empName;
private int leaveDays;
private String reason;
public LeaveRequest(String empName, int leaveDays, String reason) {
super();
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
领导的类
public abstract class Leader {
protected String name;
protected Leader nextLeader; //责任链上的后继对象
public Leader(String name) {
super();
this.name = name;
}
//设定责任链上的后继对象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
/**
* 处理请求的核心的业务方法
* @param request
*/
public abstract void handleRequest(LeaveRequest request);
}
public class Director extends Leader {
public Director(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<3){
System.out.println("学生:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("主任:"+this.name+",审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
public class Manager extends Leader {
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<7){
System.out.println("学生:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("院长:"+this.name+",审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
public class GeneralManager extends Leader {
public GeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<30){
System.out.println("学生:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("校长:"+this.name+",审批通过!");
}else{
System.out.println(request.getEmpName()+"回家休学,居然请假"+request.getLeaveDays()+"天!");
}
}
}
总结:
1、可以随时增加新的管理层
2、servlet过滤器的处理
迭代模式(iterator)
分为聚合对象(用于存储数据)和迭代器(用于遍历数据)。在set/map/list这些数据结构中都有应用
Demo-自定义迭代器
public interface MyIterator {
void first(); //将游标指向第一个元素
void next(); //将游标指向下一个元素
boolean hasNext();//判断是否存在下一个元素
boolean isFirst();
boolean isLast();
Object getCurrentObj(); //获取当前游标指向的对象
}
public class ConcreteMyAggregate {
private List<Object> list = new ArrayList<Object>();
public void addObject(Object obj){
this.list.add(obj);
}
public void removeObject(Object obj){
this.list.remove(obj);
}
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
//获得迭代器
public MyIterator createIterator(){
return new ConcreteIterator();
}
//使用内部类定义迭代器,可以直接使用外部类的属性
private class ConcreteIterator implements MyIterator {
private int cursor; //定义游标用于记录遍历时的位置,类似于c语言的指针
@Override
public void first() {
cursor = 0;
}
@Override
public Object getCurrentObj() {
return list.get(cursor);
}
@Override
public boolean hasNext() {
if(cursor<list.size()){
return true;
}
return false;
}
@Override
public boolean isFirst() {
return cursor==0?true:false;
}
@Override
public boolean isLast() {
return cursor==(list.size()-1)?true:false;
}
@Override
public void next() {
if(cursor<list.size()){
cursor++;
}
}
}
}
中介者模式
一个项目不可能只是一个部门完成。需要多个部门完成,但是部门之间不应该乱七八糟地混为一滩,就需要一个中间者调动每个部门的事情。这个中间者就是中介者。这是结构就变成了这样
总结:
1、解耦同级对象之间的交互关系。每个对象都持有中介者对象的引 用,有什么事情只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系
2、MVC中controller就是中介者模式
命令模式
对这个有很多理论的解释,就不说理论了。举个例子:国家给地方政府下达命令,地方政府不需要知道时谁下达的命令,国家也不需要知道是谁去执行命令,双方只要通过命令达成协议就行了。
写个Demo,就懂了
//调用者/发起者
public class Invoke {
private Command command; //也可以通过容器List容纳很多命令对象,进行批处理。数据库底层的事务管理就是类似的结构!
public Invoke(Command command) {
super();
this.command = command;
}
//业务方法 ,用于调用命令类的方法
public void call(){
command.execute();
}
}
public interface Command {
/**
* 这个方法是一个返回结果为空的方法。
* 实际项目中,可以根据需求设计多个不同的方法
*/
void execute();
}
class ConcreteCommand implements Command {
private Receiver receiver; //命令的真正的执行者
public ConcreteCommand(Receiver receiver) {
super();
this.receiver = receiver;
}
@Override
public void execute() {
//命令真正执行前或后,执行相关的处理!
receiver.action();
}
}
//真正执行的人,是不需要跟invoker有联系的
public class Receiver {
public void action(){
System.out.println("Receiver.action()");
}
}
策略模式
策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法。并且由客户端决定调用哪个算法。 举个例子:在售货过程中,对新客户可能会给原价,对小批量的用户会打9折,对老客户会打8.5折,对老客户大批量打8折。用大量的if-else确实可以实现策略模式,但是代码可读性不强。策略模式就是专门用于实现算法分离。由client去选择策略。
状态模式
解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
Context:维护一个State对象,让该对象的状态随时改变
ConcreteState具体状态类:每一个类又是一个状态
备忘录模式
Ctrl+Z相信大家都使用过,怎么把刚刚修改的东西恢复成原状,这就是备忘录模式。实现原理:保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。
Emp:为当前使用的对象
EmpMements是备忘对象,与emp属性保持一致
CareTaker:负责管理备忘的对象
Demo
//当前对象
public class Emp {
private String ename;
private int age;
private double salary;
//进行备忘操作,并返回备忘录对象
public EmpMemento memento(){
return new EmpMemento(this);
}
//进行数据恢复,恢复成制定备忘录对象的值
public void recovery(EmpMemento mmt){
this.ename = mmt.getEname();
this.age = mmt.getAge();
this.salary = mmt.getSalary();
}
public Emp(String ename, int age, double salary) {
super();
this.ename = ename;
this.age = age;
this.salary = salary;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
//管理备忘录
public class CareTaker {
private EmpMemento memento;
// private List list = new ArrayList();
public EmpMemento getMemento() {
return memento;
}
public void setMemento(EmpMemento memento) {
this.memento = memento;
}
}
//备忘录类
public class EmpMemento {
private String ename;
private int age;
private double salary;
public EmpMemento(Emp e) {
this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSalary();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
public static void main(String[] args) {
CareTaker taker = new CareTaker();
Emp emp = new Emp("atcain", 20,0);
System.out.println("第一次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());//第一次打印对象:atcain---20---0
taker.setMemento(emp.memento()); //备忘一次
emp.setAge(15);
emp.setEname("hello");
emp.setSalary(9000);
System.out.println("第二次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());//第二次打印对象:hello---15---9000
emp.recovery(taker.getMemento()); //恢复到备忘录对象保存的状态
System.out.println("第三次打印对象:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary());//第三次打印对象:atcain---20---0
}
剩下解释器模式、访问者模式、模板方法模式很少见到过,如果以后学习的过程中遇到了,再加进来