在程序需要进行扩展的时候,不能够去修改原有的代码,实现一个热插拔的效果。接口和抽象类就是开闭原则最好的体现。
子类可以扩展父类的功能,但是不能改变父类原有的功能。通俗来说就是子类重写了父类的方法,在添加新的功能的同时,尽量不要重写父类的方法。如果通过重写父类的方法完成新功能,这样的方法看起来简单,但是会使继承体系可复用性变得比较差。
依赖倒转原则按照业务逻辑来说,比如说我们开发用到的三层架构,我们会调用Service接口,Service接口让他的子类再进行业务方法的实现,可以实现热插拔。
如果两个软件实体无需直接通信,那么就不应当发生直接的互相调用,可以通过第三方转发该调用,目的是为了降低类之间的耦合度,提高模块的相对独立性。在生活中,我们租房不是直接找房东,会有房屋中介,房屋中介和房东进行沟通。
采用组合或者聚合等关联实现复用有采用类的继承实现复用,但是会破坏了类的封装性,子类与父类的耦合度降低,限制了复用的灵活性
单例设计模式是这个对象只会被创建一次。实现是通过私有构造方法,然后在类中创建一个对象,提供一个静态方法,返回这个对象,给外界使用。
饿汉式是一种在类加载的时候,就已经把对象创建出来,调用者使用。
单例设计模式-饿汉式-静态成员变量
publicclassSingleton{//单例设计模式-饿汉式-静态成员变量实现privateSingleton(){}privatestaticSingletonsingleton=newSingleton();publicstaticSingletongetInstance(){returnsingleton;}}单例设计模式-饿汉式-静态代码块
publicclassSingletonDemo2{//单例设计模式-饿汉式-静态代码块privateSingletonDemo2(){}privatestaticSingletonDemo2instance;static{instance=newSingletonDemo2();}publicstaticSingletonDemo2getInstance(){returninstance;}}单例设计模式-懒汉式懒汉式是调用实例方法的时候才会被创建对象单例设计模式-懒汉式-双重加锁方式我使用是双重加锁的懒汉式
单例设计模式-懒汉式-静态内部类静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性
publicclassSingletonStatic{//单例模式-饿汉式-静态内部类privateSingletonStatic(){}privatestaticclassSingletonHolder{privatestaticfinalSingletonStaticINSTANCE=newSingletonStatic();}publicstaticSingletonStaticgetInstance(){returnSingletonHolder.INSTANCE;}}单例设计模式-饿汉式-枚举类
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
publicclassSingletonStatic{privatestaticbooleanflag=false;//默认为false//单例模式-饿汉式-静态内部类privateSingletonStatic(){//如果为true就说明已经创建过一次了,就抛出异常不让他创建了//考虑到多线程情况下,flag变量是共享资源,可能造成线程安全问题,于是进行加锁synchronized(SingletonStatic.class){if(flag){thrownewRuntimeException("It'snotFirstCreat");}flag=true;}}privatestaticclassSingletonHolder{privatestaticfinalSingletonStaticINSTANCE=newSingletonStatic();}publicstaticSingletonStaticgetInstance(){returnSingletonHolder.INSTANCE;}//在Singleton类中添加`readResolve()`方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。privateObjectreadResolve(){returnSingletonHolder.INSTANCE;}}jdk源码中RunTime类就使用的是单例模式。(使用的是饿汉式中的静态成员变量)
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
具体实现代码:
抽象咖啡类:
/***@Autor:wl*@Date:2021/8/1421:02*@Version1.0*/publicabstractclassCoffee{publicabstractStringgetName();//加糖publicvoidaddSugar(){System.out.println("加糖");}//加奶publicvoidaddMilk(){System.out.println("加奶");}}咖啡商店类
/***@Autor:wl*@Date:2021/8/1421:08*@Version1.0*/publicclassCoffeeStore{//点咖啡方法publicCoffeeorderCoffer(Stringtype){//调用工厂模式来创建咖啡Coffeecoffee=SimpleCoffeeFactory.createCoffee(type);coffee.addMilk();coffee.addSugar();returncoffee;}}具体的实现类-拿铁咖啡
/***@Autor:wl*@Date:2021/8/1421:05*@Version1.0*/publicclassLatteCoffeeextendsCoffee{@OverridepublicStringgetName(){return"拿铁咖啡";}}简单咖啡工厂
/***@Autor:wl*@Date:2021/8/169:01*@Version1.0*/publicclassSimpleCoffeeFactory{//简单工厂模式publicstaticCoffeecreateCoffee(Stringtype){Coffeecoffee=null;if("拿铁咖啡".equals(type)){coffee=newLatteCoffee();}elseif("美式咖啡".equals(type)){coffee=newAmericanCoffee();}else{thrownewRuntimeException("Error");}returncoffee;}}美式咖啡
/***@Autor:wl*@Date:2021/8/1421:07*@Version1.0*/publicclassAmericanCoffeeextendsCoffee{@OverridepublicStringgetName(){return"美式咖啡";}}测试类
/***@Autor:wl*@Date:2021/8/1421:11*@Version1.0*/publicclassTestCoffee{publicstaticvoidmain(String[]args){Coffeecoffee=newCoffeeStore().orderCoffer("拿铁咖啡");System.out.println(coffee.getName());}}工厂方法模式(适用于同一类产品)工厂方法模式是对简单工厂模式的改进,它定义了一个工厂接口,让产品工厂实现他的方法,创建对应的产品,实现解耦合。具体实现代码:
抽象咖啡工厂
/***@Autor:wl*@Date:2021/8/1615:17*@Version1.0*/publicinterfaceCoffeeFactory{//咖啡工厂CoffeecreateCoffee();}抽象咖啡工程抽象类
/***@Autor:wl*@Date:2021/8/1615:20*@Version1.0*/publicclassAmericanCoffeeFactoryimplementsCoffeeFactory{//生产美式咖啡@OverridepublicCoffeecreateCoffee(){returnnewAmericanCoffee();}}/***@Autor:wl*@Date:2021/8/1615:21*@Version1.0*/publicclassLatteCoffeeFactoryimplementsCoffeeFactory{//生产拿铁咖啡@OverridepublicCoffeecreateCoffee(){returnnewLatteCoffee();}}咖啡实体类
/***@Autor:wl*@Date:2021/8/1421:02*@Version1.0*/publicabstractclassCoffee{publicabstractStringgetName();//加糖publicvoidaddSugar(){System.out.println("加糖");}//加奶publicvoidaddMilk(){System.out.println("加奶");}}抽象咖啡类
/***@Autor:wl*@Date:2021/8/1421:02*@Version1.0*/publicabstractclassCoffee{publicabstractStringgetName();//加糖publicvoidaddSugar(){System.out.println("加糖");}//加奶publicvoidaddMilk(){System.out.println("加奶");}}咖啡实现类
/***@Autor:wl*@Date:2021/8/1421:07*@Version1.0*/publicclassAmericanCoffeeextendsCoffee{@OverridepublicStringgetName(){return"美式咖啡";}}/***@Autor:wl*@Date:2021/8/1421:05*@Version1.0*/publicclassLatteCoffeeextendsCoffee{@OverridepublicStringgetName(){return"拿铁咖啡";}}咖啡商店
/***@Autor:wl*@Date:2021/8/1421:08*@Version1.0*/publicclassCoffeeStore{privateCoffeeFactoryfactory;publicvoidsetFactory(CoffeeFactoryfactory){this.factory=factory;}//点咖啡方法publicCoffeeorderCoffer(){//调用工厂模式来创建咖啡Coffeecoffee=factory.createCoffee();coffee.addMilk();coffee.addSugar();returncoffee;}}测试类
/***@Autor:wl*@Date:2021/8/1421:11*@Version1.0*/publicclassTestCoffee{publicstaticvoidmain(String[]args){CoffeeStorestore=newCoffeeStore();store.setFactory(newLatteCoffeeFactory());Coffeecoffee=store.orderCoffer();System.out.println(coffee.getName());}}优点:
缺点:
抽象产品类
/***@Autor:wl*@Date:2021/8/1616:21*@Version1.0*/publicinterfaceDessertFactory{//抽象工厂,提供创建咖啡和创建甜品的方法CoffeecreateCoffee();DessertcreateDessert();}美式风味生产工厂类
/***@Autor:wl*@Date:2021/8/1616:28*@Version1.0*/publicclassAmericanDessertFactoryimplementsDessertFactory{//美式甜品工厂//创建咖啡的方法@OverridepublicCoffeecreateCoffee(){returnnewAmericanCoffee();}//创建甜品的方法@OverridepublicDessertcreateDessert(){returnnewMatchaMousse();}}意大利风味生产工厂类
/***@Autor:wl*@Date:2021/8/1616:37*@Version1.0*/publicclassItalyDessertFactoryimplementsDessertFactory{//意大利甜品风味工厂@OverridepublicCoffeecreateCoffee(){returnnewLatteCoffee();}//创建甜品方法@OverridepublicDessertcreateDessert(){returnnewTiramisu();}}咖啡抽象类
/***@Autor:wl*@Date:2021/8/1421:02*@Version1.0*/publicabstractclassCoffee{publicabstractStringgetName();//加糖publicvoidaddSugar(){System.out.println("加糖");}//加奶publicvoidaddMilk(){System.out.println("加奶");}}美式风味实体类
/***@Autor:wl*@Date:2021/8/1421:07*@Version1.0*/publicclassAmericanCoffeeextendsCoffee{@OverridepublicStringgetName(){return"美式咖啡";}}/***@Autor:wl*@Date:2021/8/1616:34*@Version1.0*/publicclassMatchaMousseextendsDessert{@Overridepublicvoidshow(){System.out.println("抹茶莫斯");}}意式风味实体类
/***@Autor:wl*@Date:2021/8/1421:05*@Version1.0*/publicclassLatteCoffeeextendsCoffee{@OverridepublicStringgetName(){return"拿铁咖啡";}}/***@Autor:wl*@Date:2021/8/1616:35*@Version1.0*/publicclassTiramisuextendsDessert{@Overridevoidshow(){System.out.println("提拉米苏");}}测试类
/***@Autor:wl*@Date:2021/8/1616:43*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){DessertFactoryfactory=newItalyDessertFactory();Coffeecoffee=factory.createCoffee();System.out.println(coffee.getName());factory.createDessert().show();}}优点:
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
具体的代码实现:
咖啡工厂:
/***@Autor:wl*@Date:2021/8/1618:05*@Version1.0*@Describe简单工厂的改进和配置文件进行配合使用*/publicclassCoffeeFactory{privatestaticMap
/***@Autor:wl*@Date:2021/8/1421:02*@Version1.0*/publicabstractclassCoffee{publicabstractStringgetName();//加糖publicvoidaddSugar(){System.out.println("加糖");}//加奶publicvoidaddMilk(){System.out.println("加奶");}}美式咖啡
/***@Autor:wl*@Date:2021/8/1421:07*@Version1.0*/publicclassAmericanCoffeeextendsCoffee{@OverridepublicStringgetName(){return"美式咖啡";}}拿铁咖啡
/***@Autor:wl*@Date:2021/8/1421:05*@Version1.0*/publicclassLatteCoffeeextendsCoffee{@OverridepublicStringgetName(){return"拿铁咖啡";}}测试类
/***@Autor:wl*@Date:2021/8/1618:25*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){Coffeecoffee=CoffeeFactory.creatCoffee("american");System.out.println(coffee.getName());}}Peoperties配置文件:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
原型模式的克隆分为浅克隆和深克隆。
Java中的Object类中提供了clone()方法来实现浅克隆。Cloneable接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类
实体类
/***@Autor:wl*@Date:2021/8/1711:14*@Version1.0*@Describe原型模式-浅拷贝*/publicclassPeopleimplementsCloneable{privateStringname;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}@OverrideprotectedPeopleclone()throwsCloneNotSupportedException{return(People)super.clone();}publicvoidshow(){System.out.println(name+"被颁发三好学生奖状");}}测试类
/***@Autor:wl*@Date:2021/8/1711:14*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Peoplepeople=newPeople();people.setName("张三");Peoplepeople1=people.clone();people1.setName("李四");people.show();people1.show();}}深克隆使用序列化实现深克隆
/***@Autor:wl*@Date:2021/8/1711:23*@Version1.0*@Describe*/publicclassStudentimplementsSerializable{privateStringname;publicStudent(Stringname){this.name=name;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+'}';}}/***@Autor:wl*@Date:2021/8/1711:25*@Version1.0*@Describe*/publicclassPeopleimplementsCloneable,Serializable{privateStudentstudent;publicStudentgetStudent(){returnstudent;}publicvoidsetStudent(Studentstudent){this.student=student;}@OverrideprotectedPeopleclone()throwsCloneNotSupportedException{return(People)super.clone();}publicvoidshow(){System.out.println(student.getName()+"被颁发三好学生奖状");}}测试类
/***@Autor:wl*@Date:2021/8/1711:28*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args)throwsException{Peoplepeople=newPeople();people.setStudent(newStudent("王五"));//输出流ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("D:\\ideaProject\\design\\src\\main\\java\\com\\wl\\design\\3.txt"));oos.writeObject(people);//输入流读取ObjectInputStreamois=newObjectInputStream(newFileInputStream("D:\\ideaProject\\design\\src\\main\\java\\com\\wl\\design\\3.txt"));Peoplepeople1=(People)ois.readObject();people1.getStudent().setName("123456");people.show();people1.show();}}建造者模式建造者模式-结构抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
具体建造者类(ConcreteBuilder):实现Builder接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
产品类(Product):要创建的复杂对象。
指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
抽象建造者类(Builder):
publicabstractclassBuilder{//提供给子类使用protectedBikebike=newBike();//建造车架abstractvoidbuildFrame();//建造车座abstractvoidbuildSeat();//创建自行车abstractBikecreateBike();}具体建造者类(ConcreteBuilder):
publicclassMobikeBuilderextendsBuilder{@OverridevoidbuildFrame(){bike.setFrame("膜拜车架");}@OverridevoidbuildSeat(){bike.setSeat("膜拜座椅");}@OverrideBikecreateBike(){returnbike;}}publicclassOfoBuilderextendsBuilder{@OverridevoidbuildFrame(){bike.setFrame("小黄车车架");}@OverridevoidbuildSeat(){bike.setSeat("小黄车座椅");}@OverrideBikecreateBike(){returnbike;}}产品类(Product):
publicclassBike{//车架privateStringframe;//车椅privateStringseat;publicStringgetFrame(){returnframe;}publicvoidsetFrame(Stringframe){this.frame=frame;}publicStringgetSeat(){returnseat;}publicvoidsetSeat(Stringseat){this.seat=seat;}@OverridepublicStringtoString(){return"Bike{"+"frame='"+frame+'\''+",seat='"+seat+'\''+'}';}}指挥者类(Director):
publicclassDirector{privateBuilderbuilder;publicDirector(Builderbuilder){this.builder=builder;}publicBikeconstruct(){builder.buildFrame();builder.buildSeat();returnbuilder.createBike();}}测试类(用户类):
publicclassTest{publicstaticvoidmain(String[]args){//调用指挥类进Directordirector=newDirector(newMobikeBuilder());//调用指挥类中的建造方法Bikebike=director.construct();System.out.println(bike.getFrame());}}优点:
在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构
建造者类:
publicclassTest{publicstaticvoidmain(String[]args){//这样就可以使用链式编程可以根据自己想要的进行构建Computercomputer=newComputer.ComputerBuilder().cpu("intel").mainboard("联想").build();}}小结工厂方法模式vs建造者模式工厂方法模式注重的是整体对象的创建方式,而建造者模式注重的是部件构建的过程,意在一步一步地精确构造创建出一个复杂的对象。
举个简单的例子,在生活中,我们要构建一辆汽车,工厂模式会将对应的汽车交给对应的厂商进行创建,而建造者模式是注重一个个组件拼接起来一辆汽车。java语言就好比如工厂模式,c++就像是建造者模式。
抽象工厂不关心构建过程,只关心什么产品由什么工厂生产即可,建造者模式则是要求按照指定的蓝图构造产品,它的主要目的是通过组装配件生产一个新的产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车
代理模式在我们的生活中处处可见,例如我们租房子不是直接找房东而是找中介,买手机找对应的代理商,去对应的店铺买等等
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。
卖票接口:
/***@Autor:wl*@Date:2021/8/1915:56*@Version1.0*@Describe卖票接口*/publicinterfaceSellTickets{//卖票方法intsellTicket();}目标类:
/***@Autor:wl*@Date:2021/8/1915:57*@Version1.0*@Describe目标类-火车站*/publicclassStationimplementsSellTickets{@OverridepublicintsellTicket(){//火车站是目标类对象,假设一张票卖20System.out.println("卖票");return20;}}代理类:
/***@Autor:wl*@Date:2021/8/1915:58*@Version1.0*@Describe静态代理类-火车票代售点*/publicclassStaticProxyimplementsSellTickets{//目标类privateStationstation=newStation();//火车站的售票@OverridepublicintsellTicket(){intprice=station.sellTicket();price+=10;System.out.println("代售点多收了10块服务费");returnprice;}}测试类:
/***@Autor:wl*@Date:2021/8/1916:01*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){//调用代理类StaticProxystaticProxy=newStaticProxy();intprice=staticProxy.sellTicket();System.out.println(price);}}代理模式-JDK动态代理(如果没有接口,不能使用jdk动态代理)接口:
/***@Autor:wl*@Date:2021/8/1916:16*@Version1.0*@Describe卖票类*/publicinterfaceSellTickets{//卖票方法intsellTicket();}代理类:
/***@Autor:wl*@Date:2021/8/1915:58*@Version1.0*@Describe静态代理类-火车票代售点*/publicclassJDKProxyimplementsInvocationHandler{//目标类对象privateObjecttarget=null;publicJDKProxy(Objecttarget){this.target=target;}//代理类@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{/*Objectproxy代理对象Methodmethod对应于在代理对象上调用的接口方法的Method实例Object[]args代理对象调用接口方法时传递的实际参数*/Objectresult=method.invoke(target,args);//方法的增强if(result!=null){intprice=(Integer)result;price+=10;result=price;}System.out.println("代理多收10块钱");returnresult;}}目标类
/***@Autor:wl*@Date:2021/8/1915:57*@Version1.0*@Describe目标类-火车站*/publicclassStationimplementsSellTickets{@OverridepublicintsellTicket(){//火车站是目标类对象,假设一张票卖20System.out.println("卖票");return20;}}测试类
/***@Autor:wl*@Date:2021/8/1916:36*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){/*ClassLoaderloader,类加载器Class<>[]interfaces,接口的字节码对象InvocationHandlerh代理类*/Stationstation=newStation();//动态代理类JDKProxyproxy=newJDKProxy(station);SellTicketsst=(SellTickets)Proxy.newProxyInstance(station.getClass().getClassLoader(),station.getClass().getInterfaces(),proxy);intprice=st.sellTicket();System.out.println(price);}}代理模式-CGLIB动态代理(对jdk动态代理的补充,适用于没接口的)此处省略1000行代码
适配器模式(Adapter)包含以下主要角色:
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
目标接口
SDCard:
/***@Autor:wl*@Date:2021/8/2016:25*@Version1.0*@DescribeSDCard接口*/publicinterfaceSDCard{//读卡StringreadSDCard();//写卡voidwriteSDCard(Stringmsg);}TFCard:
/***@Autor:wl*@Date:2021/8/2016:22*@Version1.0*@DescribeTFCard接口*/publicinterfaceTFCard{//读卡StringreadTFCard();//写卡voidwriteTFCard(Stringmsg);}适配者TFCardImpl:
/***@Autor:wl*@Date:2021/8/2016:49*@Version1.0*@DescribeTFCard具体的实现类*/publicclassTFCardImplimplementsTFCard{@OverridepublicStringreadTFCard(){Stringmsg="readTFCard";returnmsg;}@OverridepublicvoidwriteTFCard(Stringmsg){System.out.println(msg);}}SDCardImpl:
/***@Autor:wl*@Date:2021/8/2016:48*@Version1.0*@DescribeSDCard的具体实现类*/publicclassSDCardImplimplementsSDCard{@OverridepublicStringreadSDCard(){Stringmsg="readsdCard";returnmsg;}@OverridepublicvoidwriteSDCard(Stringmsg){System.out.println(msg);}}computer:
/***@Autor:wl*@Date:2021/8/2016:52*@Version1.0*@DescribeComputer读卡类,只能读取SD卡*/publicclassComputer{publicStringreadSDCard(SDCardsdCard){if(sdCard==null){thrownewNullPointerException("sdcardnull");}returnsdCard.readSDCard();}}适配器类
/***@Autor:wl*@Date:2021/8/2016:53*@Version1.0*@Describe适配器类*/publicclassAdapterextendsTFCardImplimplementsSDCard{//适配器类将本来电脑读不到TF卡的内容的数据,进行处理,从来让电脑读到TF卡的内容@OverridepublicStringreadSDCard(){System.out.println("adpaterread");returnreadTFCard();}@OverridepublicvoidwriteSDCard(Stringmsg){System.out.println("adpaterwrite");writeTFCard(msg);}}测试类
/***@Autor:wl*@Date:2021/8/2016:58*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){Computercomputer=newComputer();Stringmsg=computer.readSDCard(newAdapter());System.out.println(msg);}}类适配器违背了合成复用原则。
/***@Autor:wl*@Date:2021/8/2016:53*@Version1.0*@Describe适配器类*/publicclassAdapterimplementsSDCard{//适配器类将本来电脑读不到TF卡的内容的数据,进行处理,从来让电脑读到TF卡的内容privateTFCardtfCard;//将对象传进来避免违背合成复用原则publicAdapter(TFCardtfCard){this.tfCard=tfCard;}@OverridepublicStringreadSDCard(){System.out.println("adpaterread");returntfCard.readTFCard();}@OverridepublicvoidwriteSDCard(Stringmsg){System.out.println("adpaterwrite");tfCard.writeTFCard(msg);}}测试类
/***@Autor:wl*@Date:2021/8/2016:58*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){Computercomputer=newComputer();//创建TFCard读卡TFCardtfCard=newTFCardImpl();//创建适配器类Adapteradapter=newAdapter(tfCard);Stringmsg=computer.readSDCard(adapter);System.out.println(msg);}}JDK中Reader(字符流)、InputStream(字节流)的适配使用的是InputStreamReader。
快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。使用继承的方式存在的问题:
这个时候就可以使用装饰者模式,装饰者模式结构如下:装饰(Decorator)模式中的角色:
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator)角色:继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
抽象构件:
/***@Autor:wl*@Date:2021/8/2120:04*@Version1.0*@Describe抽象构件-快餐店*/publicabstractclassFastFood{//价格privatefloatprice;//描述privateStringdesc;publicFastFood(){}publicFastFood(floatprice,Stringdesc){this.price=price;this.desc=desc;}publicfloatgetPrice(){returnprice;}publicvoidsetPrice(floatprice){this.price=price;}publicStringgetDesc(){returndesc;}publicvoidsetDesc(Stringdesc){this.desc=desc;}//定义抽象方法计算价格publicabstractfloatcost();}具体构件:
/***@Autor:wl*@Date:2021/8/2120:08*@Version1.0*@Describe具体构件-炒饭*/publicclassFastRiceextendsFastFood{publicFastRice(){//使用父类中的构造方法给属性填充super(12,"扬州炒饭");}//计算价格@Overridepublicfloatcost(){returngetPrice();}}抽象装饰:
/***@Autor:wl*@Date:2021/8/2120:30*@Version1.0*@Describe抽象装饰-搭配*/publicabstractclassDecoratorextendsFastFood{privateFastFoodfastFood;publicFastFoodgetFastFood(){returnfastFood;}publicvoidsetFastFood(FastFoodfastFood){this.fastFood=fastFood;}publicDecorator(FastFoodfastFood,floatprice,Stringdesc){super(price,desc);this.fastFood=fastFood;}}具体装饰:
/***@Autor:wl*@Date:2021/8/2120:37*@Version1.0*@Describe具体装饰-鸡蛋类*/publicclassEggextendsDecorator{publicEgg(FastFoodfastFood){super(fastFood,1,"鸡蛋");}@Overridepublicfloatcost(){returngetPrice()+getFastFood().getPrice();}@OverridepublicStringgetDesc(){returnsuper.getDesc()+getFastFood().getDesc();}}/***@Autor:wl*@Date:2021/8/2120:49*@Version1.0*@Describe具体装饰-培根*/publicclassBaconextendsDecorator{publicBacon(FastFoodfastFood){super(fastFood,2,"培根");}@Overridepublicfloatcost(){returngetPrice()+getFastFood().getPrice();}}好处:
装饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
jdk源码中BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写据的效率。
和代理模式不同的是,代理模式是对目标类的保护和隐藏,装饰者模式是对目标类的增强。
需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式
文件格式接口:
/***@Autor:wl*@Date:2021/8/2220:07*@Version1.0*@Describe抽象化角色-文件格式*/publicinterfaceVideoFile{voiddecode(Stringfilename);}具体实现类:
/***@Autor:wl*@Date:2021/8/2220:10*@Version1.0*@Describe*/publicclassAVIFileimplementsVideoFile{@Overridepublicvoiddecode(Stringfilename){System.out.println("AVI文件名为:"+filename);}}/***@Autor:wl*@Date:2021/8/2220:09*@Version1.0*@Describe*/publicclassRmvbFileimplementsVideoFile{@Overridepublicvoiddecode(Stringfilename){System.out.println("Rmvb文件名为:"+filename);}}操作系统抽象类:
/***@Autor:wl*@Date:2021/8/2220:11*@Version1.0*@Describe*/publicabstractclassOperatingSystem{protectedVideoFilevideoFile;publicOperatingSystem(VideoFilevideoFile){this.videoFile=videoFile;}publicabstractvoidplay(Stringfilename);}操作系统具体实现类:
/***@Autor:wl*@Date:2021/8/2220:12*@Version1.0*@Describe*/publicclassWindowsextendsOperatingSystem{publicWindows(VideoFilevideoFile){super(videoFile);}@Overridepublicvoidplay(Stringfilename){videoFile.decode(filename);}}/***@Autor:wl*@Date:2021/8/2220:12*@Version1.0*@Describe*/publicclassMacextendsOperatingSystem{publicMac(VideoFilevideoFile){super(videoFile);}@Overridepublicvoidplay(Stringfilename){videoFile.decode(filename);}}测试类:
/***@Autor:wl*@Date:2021/8/2220:15*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){OperatingSystemos=newWindows(newRmvbFile());os.play("战狼");}}使用场景:
外观(Facade)模式包含以下主要角色:
案例小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭:
/***@Autor:wl*@Date:2021/8/2220:55*@Version1.0*@Describe子系统(SubSystem)角色-空调*/publicclassAirCondition{publicvoidon(){System.out.println("空调已经打开");}publicvoidoff(){System.out.println("空调已经关闭");}}/***@Autor:wl*@Date:2021/8/2220:53*@Version1.0*@Describe子系统(SubSystem)角色-电灯*/publicclassLight{publicvoidon(){System.out.println("电灯已经打开");}publicvoidoff(){System.out.println("电灯已经关闭");}}/***@Autor:wl*@Date:2021/8/2220:53*@Version1.0*@Describe子系统(SubSystem)角色-电视机*/publicclassTV{publicvoidon(){System.out.println("电视机已经打开");}publicvoidoff(){System.out.println("电视机已经关闭");}}外观类:
/***@Autor:wl*@Date:2021/8/2220:58*@Version1.0*@Describe外观类-用户访问的类*/publicclassSmartApplication{privateTVtv;privateLightlight;privateAirConditionairCondition;publicSmartApplication(){this.tv=newTV();this.light=newLight();this.airCondition=newAirCondition();}publicvoidcontroller(Stringmessage){if("打开".contains(message)){on();}elseif("关闭".contains(message)){off();}else{System.out.println("我还不能听懂你的意思");}}privatevoidon(){tv.on();airCondition.on();light.on();}privatevoidoff(){tv.off();airCondition.off();light.off();}}测试类:
/***@Autor:wl*@Date:2021/8/2221:02*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){SmartApplicationsmartApplication=newSmartApplication();smartApplication.controller("打开");System.out.println("================================");smartApplication.controller("关闭");}}好处:
缺点:
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
抽象享元角色:
/***@Autor:wl*@Date:2021/8/2615:06*@Version1.0*@Describe抽象享元角色*/publicabstractclassAbstractBox{publicabstractStringgetShape();publicvoiddisplay(Stringcolor){System.out.println("方块形状:"+this.getShape()+"颜色:"+color);}}享元工厂:
/***@Autor:wl*@Date:2021/8/2615:32*@Version1.0*@Describe享元工厂*/publicclassBoxFactory{privateHashMap
/***@Autor:wl*@Date:2021/8/2615:27*@Version1.0*@Describe具体享元角色*/publicclassIBoxextendsAbstractBox{@OverridepublicStringgetShape(){return"i形状";}}/***@Autor:wl*@Date:2021/8/2615:27*@Version1.0*@Describe*/publicclassLBoxextendsAbstractBox{@OverridepublicStringgetShape(){return"L形状";}}/***@Autor:wl*@Date:2021/8/2615:27*@Version1.0*@Describe*/publicclassZBoxextendsAbstractBox{@OverridepublicStringgetShape(){return"Z形状";}}测试类:
/***@Autor:wl*@Date:2021/8/2615:35*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){BoxFactoryboxFactory=BoxFactory.getBoxFactory();AbstractBoxl=boxFactory.getBox("L");l.display("红色");AbstractBoxi=boxFactory.getBox("I");i.display("红色");}}优点:
极大减少了内存中相同或相似对象数量,节约系统资源。
享元状态中得外部状态相对独立,且不影响内部状态。
使用场景:
一个系统有大量相同或者相似的对象,造成内存的大量耗费。
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
模板方法(TemplateMethod)模式包含以下主要角色:
抽象类(AbstractClass):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
钩子方法(HookMethod):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
具体子类(ConcreteClass):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟:
抽象方法:AbstractClass:
/***@Autor:wl*@Date:2021/8/2619:04*@Version1.0*@Describe抽象类(定义模板方法和基本方法)*/publicabstractclassAbstractClass{publicfinalvoidcookie(){pullOil();UpOil();addSeason();addVegetable();fry();} //具体的方法protectedvoidpullOil(){System.out.println("倒油");}protectedvoidUpOil(){System.out.println("热油");}protectedvoidfry(){System.out.println("炒啊炒啊炒");}//抽象方法abstractvoidaddSeason(); abstractvoidaddVegetable();}具体子类:
/***@Autor:wl*@Date:2021/8/270:21*@Version1.0*@Describe*/publicclassPotatoSenceextendsAbstractClass{@OverridevoidaddSeason(){System.out.println("加醋");}@OverridevoidaddVegetable(){System.out.println("加土豆");}}/***@Autor:wl*@Date:2021/8/270:23*@Version1.0*@Describe*/publicclasstomatoextendsAbstractClass{@OverridevoidaddSeason(){System.out.println("加盐");}@OverridevoidaddVegetable(){System.out.println("加土豆");}}测试类:
publicclassTest{publicstaticvoidmain(String[]args){PotatoSenceps=newPotatoSence();ps.cookie();}}优点:
-提高代码的复用性
适用场景:
JDK源码中使用到的模板方法
InputStream类就使用了模板方法模式。在InputStream类中定义了多个read()方法。在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现
结构如下:策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
抽象策略:
publicinterfaceStrategy{voidshow();}具体的策略角色:
publicclassStrategyAimplementsStrategy{@Overridepublicvoidshow(){System.out.println("买一送二");}}publicclassStrategyBimplementsStrategy{@Overridepublicvoidshow(){System.out.println("满1000元减200");}}publicclassStrategyCimplementsStrategy{@Overridepublicvoidshow(){System.out.println("300元购物卷");}}环境角色:
publicclassSaleMan{privateStrategystrategy;publicSaleMan(Strategystrategy){this.strategy=strategy;}publicvoidsaleShow(){strategy.show();}}测试角色
publicclassTest{publicstaticvoidmain(String[]args){SaleMansaleManA=newSaleMan(newStrategyA());saleManA.saleShow();SaleMansaleManB=newSaleMan(newStrategyB());saleManB.saleShow();SaleMansaleManC=newSaleMan(newStrategyC());saleManC.saleShow();}}优点:
策略类之间可以自由切换,由于策略类都实现同一个接口,所以是他们之间可以自由切换
易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,不用修改原有的代码,并符合开闭原则
避免使用多重条件选择语句(ifelse),充分体现出面向对象设计原则
客户端必须知道所有的策略类.并自己选择一个策略类
策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量
使用场景:
在(if,else)语句过多的情况下
一个系统需要动态的在几个算法中选择一种时候,可将每个算法封装到策略类中
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
命令模式包含以下主要角色:
具体命令(ConcreteCommand)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
实现者/接收者(Receiver)角色:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
调用者/请求者(Invoker)角色:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
案例:
服务员:就是调用者角色,由她来发起命令。
资深大厨:就是接收者角色,真正命令执行的对象。
订单:命令中包含订单。
类图:
抽象命令:
/***@Autor:wl*@Date:2021-09-0110:06*@Version1.0*@Describe抽象命令*/publicinterfaceCommand{voidexecute();}具体命令:
/***@Autor:wl*@Date:2021-09-0116:57*@Version1.0*@Describe发出命令的对象*/publicclassWaitor{privateArrayList
/***@Autor:wl*@Date:2021-09-0110:09*@Version1.0*@Describe接收者对象*/publicclassSeniorChef{publicvoidmakeFood(intnum,StringfoodName){System.out.println(num+"份"+foodName);}}优点:
降低耦合,能将调用对象和操作对象解耦合
增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
使用命令模式会造成系统有过多的具体命令类
系统结构也会更加复杂
JDK源码中使用到的:
Runable是一个典型命令模式,Runnable担当命令的角色,Thread充当的是调用者,start方法就是其执行方法
职责链模式主要包含以下角色:
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
具体处理者(ConcreteHandler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
抽象处理者:
/***@Autor:wl*@Date:2021-09-0118:49*@Version1.0*@Describe抽象处理者*/publicabstractclassHandler{protectedstaticfinalintNUM_ONE=1;protectedstaticfinalintNUM_THREE=3;protectedstaticfinalintNUM_SEVEN=7;//批假区间privateintnumStart;privateintnumEnd;privateHandlernextHandler;publicHandler(intnumStart){this.numStart=numStart;}publicHandler(intnumStart,intnumEnd){this.numStart=numStart;this.numEnd=numEnd;}//设置上级领导publicvoidsetNextHandler(HandlernextHandler){this.nextHandler=nextHandler;}//各级领导请求条的方法publicabstractvoidhandle(LeaveRequestleaveRequest);//提交请假条publicfinalvoidsubmit(LeaveRequestleaveRequest){this.handle(leaveRequest);if(this.nextHandler!=null&&leaveRequest.getNum()>this.numEnd){//提交给上级领导审批this.nextHandler.submit(leaveRequest);}else{System.out.println("流程结束");}}}具体处理者:
/***@Autor:wl*@Date:2021-09-0118:45*@Version1.0*@Describe请求类*/publicclassLeaveRequest{//请假人姓名privateStringname;//请假天数privateintnum;//请假内容privateStringcontent;publicLeaveRequest(Stringname,intnum,Stringcontent){this.name=name;this.num=num;this.content=content;}publicStringgetName(){returnname;}publicintgetNum(){returnnum;}publicStringgetContent(){returncontent;}@OverridepublicStringtoString(){return"LeaveRequest{"+"name='"+name+'\''+",num="+num+",content='"+content+'\''+'}';}}测试类:
/***@Autor:wl*@Date:2021-09-0119:12*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){LeaveRequestleaveRequest=newLeaveRequest("新人小明",1,"毕业答辩");GroupLeadergroupLeader=newGroupLeader();Managermanager=newManager();Bossboss=newBoss();//设置领导等级groupLeader.setNextHandler(manager);manager.setNextHandler(boss);//小明提交请假小组长先进行审批groupLeader.submit(leaveRequest);}}优点:
降低耦合度,请求者和接收者的耦合度降低
责任分担啊,每个类只处理自己的职责
增强了系统的可扩展性
对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定的影响
不能保证每一个请求被处理,由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能会一直传到链的末端都得不到处理。
JDK源码的使用:
在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用
抽象状态(State)角色:
/***@versionv1.0*@ClassName:Context*@Description:环境角色类*@Author:wl*/publicclassContext{//定义对应状态对象的常量publicfinalstaticOpeningStateOPENING_STATE=newOpeningState();publicfinalstaticClosingStateCLOSING_STATE=newClosingState();publicfinalstaticRunningStateRUNNING_STATE=newRunningState();publicfinalstaticStoppingStateSTOPPING_STATE=newStoppingState();//定义一个当前电梯状态变量privateLiftStateliftState;publicLiftStategetLiftState(){returnliftState;}//设置当前状态对象publicvoidsetLiftState(LiftStateliftState){this.liftState=liftState;//设置当前状态对象中的Context对象this.liftState.setContext(this);}publicvoidopen(){this.liftState.open();}publicvoidclose(){this.liftState.close();}publicvoidrun(){this.liftState.run();}publicvoidstop(){this.liftState.stop();}}测试类:
/***@versionv1.0*@ClassName:Client*@Description:TODO(一句话描述该类的功能)*@Author:wl*/publicclassClient{publicstaticvoidmain(String[]args){//创建环境角色对象Contextcontext=newContext();//设置当前电梯装填context.setLiftState(newClosingState());context.open();context.run();context.close();context.stop();}}优点:
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
状态模式的使用必然会增加系统类和对象的个数。
模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
模式对"开闭原则"的支持并不太好
个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象
代码省略.............行为型设计模式-中介模式现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者
优点:
解耦合
集中控制交互
抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含hasNext()、next()等方法。
具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
抽象迭代器角色:
/***@Autor:wl*@Date:2021-09-0221:19*@Version1.0*@Describe抽象迭代器角色*/publicinterfaceStudentIterator{booleanhasNext();Studentnext();}具体迭代器角色:
/***@Autor:wl*@Date:2021-09-0221:21*@Version1.0*@Describe迭代器角色*/publicclassStudentIteratorImplimplementsStudentIterator{privateList /***@Autor:wl*@Date:2021-09-0221:24*@Version1.0*@Describe抽象聚合角色*/publicinterfaceStudentAggregate{voidaddStudent(Studentstu);voidremoveStudent(Studentstu);StudentIteratorgetStudentIterator();}具体聚合角色: /***@Autor:wl*@Date:2021-09-0221:26*@Version1.0*@Describe具体聚合角色*/publicclassStudentAggregateImplimplementsStudentAggregate{privateList /***@Autor:wl*@Date:2021-09-0221:19*@Version1.0*@Describe学生类对象*/publicclassStudent{privateStringname;privateStringnum;publicStudent(Stringname,Stringnum){this.name=name;this.num=num;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetNum(){returnnum;}publicvoidsetNum(Stringnum){this.num=num;}@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+",num='"+num+'\''+'}';}}测试类: /***@Autor:wl*@Date:2021-09-0221:28*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){StudentAggregateImplsa=newStudentAggregateImpl();sa.addStudent(newStudent("小王","1001"));sa.addStudent(newStudent("小李","1002"));sa.addStudent(newStudent("校长","1003"));//获取迭代器对象StudentIteratorstudentIterator=sa.getStudentIterator();while(studentIterator.hasNext()){Studentstu=studentIterator.next();System.out.println(stu);}}}优点: 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计 增加了类的个数,这在一定程度上增加了系统的复杂性 抽象访问者(Visitor)角色:定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。 具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。 抽象元素(Element)角色:定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。 具体元素(ConcreteElement)角色:提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。 对象结构(ObjectStructure)角色:定义当中所提到的对象结构,对象结构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问。 现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。 类图如下: 抽象元素角色: /***@Autor:wl*@Date:2021-09-0222:07*@Version1.0*@Describe抽象元素角色*/publicinterfaceAnimals{voidaccept(Personperson);}具体元素角色: /***@Autor:wl*@Date:2021-09-0222:10*@Version1.0*@Describe具体元素角色*/publicclassCatimplementsAnimals{@Overridepublicvoidaccept(Personperson){person.feed(this);System.out.println("真好吃,喵喵瞄");}}/***@Autor:wl*@Date:2021-09-0222:08*@Version1.0*@Describe具体元素角色*/publicclassDogimplementsAnimals{@Overridepublicvoidaccept(Personperson){person.feed(this);System.out.println("真好吃,汪汪汪");}}抽象访问角色: /***@Autor:wl*@Date:2021-09-0222:08*@Version1.0*@Describe抽象访问角色*/publicinterfacePerson{voidfeed(Dogdog);voidfeed(Catcat);}具体访问角色: /***@Autor:wl*@Date:2021-09-0222:11*@Version1.0*@Describe*/publicclassOwnerimplementsPerson{@Overridepublicvoidfeed(Dogdog){System.out.println("主人喂狗");}@Overridepublicvoidfeed(Catcat){System.out.println("主人喂猫");}}/***@Autor:wl*@Date:2021-09-0222:12*@Version1.0*@Describe*/publicclassVisitorimplementsPerson{@Overridepublicvoidfeed(Dogdog){System.out.println("访客喂狗");}@Overridepublicvoidfeed(Catcat){System.out.println("访客喂猫");}}测试类: /***@Autor:wl*@Date:2021-09-0222:15*@Version1.0*@Describe*/publicclassTest{publicstaticvoidmain(String[]args){Homehome=newHome();home.add(newDog());home.add(newCat());Ownerowner=newOwner();home.action(owner);}}优点: 扩展性好,在不改变对象结构中的元素的情况下,为对象结构中的元素添加新的功能 复用性好,通过访问者来定义整个对象结构的通用功能,从而提高复用成程度 对象结构变化很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则” 违反了依赖倒置原则,访问者模式依赖了具体类,而没有依赖抽象类。 对象结构相对稳定,但其操作算法经常变化的程序 备忘录模式的主要角色如下: 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。 备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。 窄接口: /***@Autor:wl*@Date:2021-09-0316:55*@Version1.0*@Describe窄接口*/publicinterfaceMemento{}发起人角色: /***@Autor:wl*@Date:2021-09-0317:04*@Version1.0管理者角色*@Describe*/publicclassRoleStateCaretaker{privateMementomemento;publicMementogetMemento(){returnmemento;}publicvoidsetMemento(Mementomemento){this.memento=memento;}}测试类 /***@Autor:wl*@Date:2021-09-0317:05*@Version1.0*@Describe测试类*/publicclassTest{publicstaticvoidmain(String[]args){System.out.println("=============大战boss前============");GameRolegameRole=newGameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretakerstateCaretaker=newRoleStateCaretaker();stateCaretaker.setMemento(gameRole.saveState());System.out.println("==============大战boss后=========");//大战boss后gameRole.fight();gameRole.stateDisplay();System.out.println("==============恢复之前的状态=========");//恢复之前的状态gameRole.recoverState(stateCaretaker.getMemento());gameRole.stateDisplay();}}优点: 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。