策略设计模式用实例理解(更好的理解接口,解耦合)
Whattodo?
接下来我们聊一聊对象数组排序,有排序那肯定有两两比较的过程;对于对象而言,其中有很多属性方法,我们可以通过某个属性进行比较,进而实现对对象数组的排序,对于下面程序:Studentstu1newStudent(张三,12,153。4);Studentstu2newStudent(李四,14,163。4);Studentstu3newStudent(王五,13,123。4);Studentstu4newStudent(赵六,4,6。4);Student〔〕stus{stu1,stu2,stu3,stu4};复制代码
Student类中的有参构造器中的参数分别为:姓名、年龄、身高;其中stus就是对象数组,现在我需要通过年龄对stus数组进行排序,那么我们就可以通过冒泡排序实现,具体如下:for(inti0;istus。length1;i){for(intj0;jstus。lengthi1;j){if(stus〔j〕。agestus〔j1〕。age){Studenttempstus〔j〕;stus〔j〕stus〔j1〕;stus〔j1〕temp;}}}复制代码
通过上述代码我们就可以将对象数组通过年龄进行排序;在上述代码中比较年龄就是一个比较策略,我们可以改变策略,比如比较身高,那么我们就需要让对象调用属性height。Howtodo?
承接上述,我们就以对象数组排序来慢慢理解策略设计模式;
首先我们创建User类,存放属性和方法,做为实例化对象的原始类;publicclassUser{创建属性privateStringname;privateintage;privateintheight;创建构造方法(若自己建立有参构造,须把无参构造加上)publicUser(){}publicUser(Stringname,intage,intheight){this。namename;this。ageage;this。heightheight;}创建getset方法publicStringgetName(){returnname;}publicvoidsetName(Stringname){this。namename;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this。ageage;}publicintgetHeight(){returnheight;}publicvoidsetHeight(intheight){this。heightheight;}重写toString()方法OverridepublicStringtoString(){returnUser{namename,ageage,heightheight};}}复制代码
创建Client类,用来实例化对象,封装对象数组,然后实现通过年龄进行进行对象数组排序;publicclassClient{主函数中创建对象,封装对象数组publicstaticvoidmain(String〔〕args){创建User对象Useruser1newUser(张三,12,153);Useruser2newUser(李四,14,163);Useruser3newUser(王五,13,133);Useruser4newUser(赵六,11,143);封装对象数组User〔〕users{user1,user2,user3,user4};通过年龄对数组进行正向排序for(inti0;iusers。length1;i){for(intj0;jusers。lengthi1;j){if(users〔j〕。getAge()users〔j1〕。getAge()){Usertempusers〔j〕;users〔j〕users〔j1〕;users〔j1〕temp;}}}输出查看对象数组是否排序成功System。out。println(Arrays。toString(users));}}复制代码
结果如下:
存在问题:我们可以看到已经排序成功;但是上述代码存在的问题是【将实现的细节暴露给了客户,并且比较也应该更面向对象一点】
解决方法:对于比较来说,我们可以改进比较方法,对比较进行封装,使得改变比较策略,而不复写代码;在此我们可以联想到equals()方法,因此我们可以抽离出一个让两个User对象进行比较的接口Comparable,然后让User实现该接口,从而完善Client中的比较细节;
创建Comparable接口,定义compare()方法;publicinterfaceComparable{封装比较方法,传入User对象,实现与调用对象的比较Integercompare(Objectobject);}复制代码
更新User类,让其继承Comparable接口,实现compare()方法;publicclassUserimplementsComparable{同之前的一样,就是增加了下面的这个方法返回两个对象的比较的结果OverridepublicIntegercompare(Objectobject){如果传进来的是一个User类型的对象,那么再继续往下比,否则直接返回nullif(objectinstanceofUser){首先将传进来的Object类型转换为User类型Useruser(User)object;返回比较的结果returnthis。getAge()user。getAge();}returnnull;}}复制代码
Clien类中的排序方法中的比较就变得更加简洁,users〔j〕。compare(users〔j1〕)通过年龄对数组进行正向排序for(inti0;iusers。length1;i){for(intj0;jusers。lengthi1;j){if(users〔j〕。compare(users〔j1〕)0){Usertempusers〔j〕;users〔j〕users〔j1〕;users〔j1〕temp;}}}复制代码
上述这种写法的好处:让所有对象拥有了可比较的能力(这也是接口的好处),并且将具比的东西转给了对象;
问题:如果按其他属性去排序,也就是改变排序策略,那么还是得去修改User中的compaer方法,这样还是不符合开闭原则,且细节还是暴露在外边,扩展性也差;
截至目前的两个问题:细节暴露在Client类中,这就很不面向对象;(宗旨:一个方法解决一个问题,而不是一坨代码解决一个问题,即单一原则)修改比较策略时还得改动源代码,这违反了开闭原则;对于第一个问题
根据单一原则,我们构建UserSorter类,实现排序方法;publicclassUserSorter{创建实现对象排序功能的方法sortpublicvoidsort(User〔〕users){通过年龄对数组进行正向排序for(inti0;iusers。length1;i){for(intj0;jusers。lengthi1;j){if(users〔j〕。compare(users〔j1〕)0){Usertempusers〔j〕;users〔j〕users〔j1〕;users〔j1〕temp;}}}}}复制代码
因此我们可将Client中实现比较的代码转换为userSorter。sort(users)publicclassClient{主函数中创建对象,封装对象数组publicstaticvoidmain(String〔〕args){创建User对象Useruser1newUser(张三,12,153);Useruser2newUser(李四,14,163);Useruser3newUser(王五,13,133);Useruser4newUser(赵六,11,143);封装对象数组User〔〕users{user1,user2,user3,user4};创建UserSorter对象UserSorteruserSorternewUserSorter();调用UserSorter中的sort方法实现排序userSorter。sort(users);输出查看对象数组是否排序成功System。out。println(Arrays。toString(users));}}复制代码
截至目前,我们就将实现细节屏蔽在Usersorter类中,并且让排序方法面向对象实现;对于第二个问题
既然在改变比较策略时不想修改User类中的代码,那么我们就不让User类去实现Comparable接口;然后比较器UserSorer中的sort方法就得改!如果我们将sort中的比较策略写死,比如就写比较年龄,那我们改变比较策略的时候,比如想比较身高,那么就一定会对代码进行修改,但我们要对修改关闭,因此我们可以将User中的比较方法抽出去,让其不具备比较的能力;那么我们就可以想到建立一个类,让其拥有比较两个对象的能力,最终我们只需要将users〔j〕,users〔j1〕,做为参数传入建立夫人那个类中的哪个方法中,最终让它返回比较的结果就行;
这样就对User本身无侵入性,就实现了不改动User代码;
我们现在定义这个类叫做Comparator,其中比较两个对象的方法为compare(Useruser,Useruser1);publicclassComparator{publicIntegercompare(Useruser,Useruser1){returnuser。getAge()user1。getAge();}}复制代码
那么UserSorter中的sort方法就需要加参数,改进比较;publicclassUserSorter{创建实现对象排序功能的方法sortpublicvoidsort(User〔〕users,Comparatorcomparator){通过年龄对数组进行正向排序for(inti0;iusers。length1;i){for(intj0;jusers。lengthi1;j){if(comparator。compare(users〔j〕,users〔j1〕)0){Usertempusers〔j〕;users〔j〕users〔j1〕;users〔j1〕temp;}}}}}复制代码
Client中在调用sort方法时就直接将比较器类Comparatornew进去就可;调用UserSorter中的sort方法实现排序userSorter。sort(users,newComparator());复制代码
截至目前,我们这样写还是违背了开闭原则,【在这里我们定义方法时,参数要面向抽象、面向接口,这样扩展性强,如果面向具体实现类,那只能实现写死的那个类,扩展性就很差】;所以我们将sort(User〔〕users,Comparatorcomparator)中的第二个参数试着变成接口:
将Comparator改为接口;publicinterfaceComparator{Integercompare(Useruser,Useruser1);}复制代码
创建CompareAgeStrategy类实现Comparator接口,作用为比较年龄策略;publicclassCompareAgeStrategyimplementsComparator{publicIntegercompare(Useruser,Useruser1){returnuser。getAge()user1。getAge();}}复制代码
这样写的话,我们在UserSorter中的sort方法里传入的就是接口,那么我们在Client类中调用sort方法时传入的参数就可以new具体的比较策略,比如:实现了年龄策略比较userSorter。sort(users,newCompareAgeStrategy());复制代码
那么我们如果想比较身高的话,那就可以扩展一个比较身高策略类,传参数时直接new比较身高策略类即可,这样就满足了不对源代码进行修改,只在源代码上进行扩展,这就满足了开闭原则,整个过程也诠释了策略设计模式;
创建CompareHeightStrategy类实现Comparator接口,作用为比较身高策略;publicclassCompareHeightStrategyimplementsComparator{OverridepublicIntegercompare(Useruser,Useruser1){returnuser。getHeight()user1。getHeight();}}复制代码
修改Client中调用sort方法时穿的参数;实现了身高策略比较userSorter。sort(users,newCompareHeightStrategy());复制代码
最终结果:
至此,Client就只需要享受策略就ok;总结从开始到最终做的工作就是一个解耦合的过程;再定义一个方法时,其中的参数需要面向抽象或者接口,这样扩展性就强;若面向具体实现类,扩展性就会变差;