死板的概念就不写了,怎么简单怎么来。
首先,从名字分析,顾名思义,装饰不就是给已有的东西额外增加一些功能或属性?。而且既然是要装饰某一个类,那么肯定要有装饰类(PoliceDecoration)和被装饰类(Police)的概念了对吧。而且为了修饰被装饰类,装饰类需要持有被装饰类的对象,不然没法调用被装饰类的方法对吧?
注意这里需要针对被装饰类的方法去抽象出接口,然后让装饰类和被装饰类都去实现此接口,从而达到“装饰”的目的。
承接上边两个步骤,装饰类和被装饰类都实现了同样的接口,如果想要在装饰类中调用被装饰类的方法,装饰类就需要持有被装饰类的对象。也就是说我们需要给装饰类传入一个被装饰类对象,然后在相应的方法中调用被装饰类的相应方法即可。同时我们也可以在调用方法之前或之后添加其他的额外操作。至此装饰模式就结束了…具体看代码
比如说刚开始的时候警察吃饭前没有要求,只管到时间去吃饭就行了,调用eat()方法,但是最近由于上级指示,警察吃饭之前要先把枪上交了,为了安全。我们就可以用装饰者模式来实现这个需求。
public class Police {
public void eat(){
}
}
步骤:
1. 根据需求在调用eat()方法之前,需要先调用交枪的逻辑,也就是需要对eat()进行装饰,那么我们就针要对源类的方法抽象出接口IFunctions。
public interface IFunctions {
void eat();
}
2.修改源类Police实现IFunctions接口
public class Peolice implements IFunctions{
public void eat(){
Log.i("Peolice","-------------->吃吃吃吃");
}
}
3.定义装饰类PoliceDecoration并实现IFunctions接口,并在该类的构造方法中传入被装饰类对象,并在对应的方法中调用被装饰类的方法。如果我们在调用被装饰类的方法之前调用新方法上交枪械即可达到装饰的目的。
public class PoliceDecoration implements IFunctions {
private IFunctions mPolice;
//在构造方法中传入被装饰类对象
public PoliceDecoration(Police police) {
this.mPolice = police;
}
@Override
public void eat() {
//调用被装饰类中对应的eat()方法
handInYouGun();
if (mPolice!=null){
mPolice.eat();
}
}
private void handInYouGun(){
Log.i("PoliceDecoration","----------->上交枪械!");
}
}
比如说商场的打折活动,这算是一个比较典型的策略模式了。打折的方式有很多,有现金减免,或折扣等等。可能每一天的打折方式都不一样,我们不能每次都去重写打折类,这样就违背了开闭原则。因此,我们就可以针对打折抽象出一个接口,每一种打折方式都去实现这个接口,并重写打折逻辑。当然我们还需要一个打折管理器,在管理器类中根据传入的打折对象,返回正确的折后价。
这里我们就以商场打折作为例子,概述一下策略模式的步骤
作为用户,软件当然是越简单越好。这里我们就假设用户只需输入商品原价,选择打折方式,然后就直接输出折后价
1. 定义打折接口IDiscount接口,包含有打折方法,需要传入原价,返回折后价。
public interface IDiscount {
double salePrice(double originalPrice);
}
2. 声明具体的打折类,实现打折接口,重写打折逻辑
/**
* 类功能描述:满减--两件,第二件半价
*/
public class CashDiscount implements IDiscount {
@Override
public double salePrice(double originalPrice) {
return originalPrice * (1 + 0.5);
}
}
/**
* 类功能描述:1件8折
*/
public class CommonDiscount implements IDiscount {
@Override
public double salePrice(double originalPrice) {
return originalPrice * 0.8;
}
}
3.声明打折管理类,也实现打折接口,需要持有打折对象
/**
* 类功能描述:打折管理类
*/
public class DiscountManager implements IDiscount {
// 具体的折扣类对象
private IDiscount mDiscount;
public DiscountManager(IDiscount mDiscount) {
this.mDiscount = mDiscount;
}
@Override
public double salePrice(double originalPrice) {
return mDiscount.salePrice(originalPrice);
}
}
4. 调用
//现金减免,第二件5折
CashDiscount cashDiscount = new CashDiscount();
//单件8折
CommonDiscount commonDiscount = new CommonDiscount();
DiscountManager cashDM = new DiscountManager(cashDiscount);
DiscountManager commonDM = new DiscountManager(commonDiscount);
double cash=cashDM.salePrice(100);
double common=commonDM.salePrice(100);
Log.i("---","---------------->cash:"+cash+",common:"+common);
了解工厂模式的朋友可能会发现,类似的功能使用工厂模式照样可以实现。没错,这里使用工厂方法,无非就是增加了工厂类,有新的折扣需求的时候去重新实现工厂接口和折扣接口即可。也不算太麻烦。而且也算是条理清晰。所以说很多时候设计模式并不是只有一种选择 。我们需要理解每一种设计模式的优缺点,在使用的时候尽量选择最适合即可。
这里可以总结一下策略模式相对于工厂模式的优点:
优点:
1. 相对来说实现类比抽象工厂方法模式少,也就是工作量会小一点
2. 从理解上来说,策略模式比抽象工厂方法更直接一点。工厂模式需要通过工厂类去创建需要的对象,而策略模式是用户自己创建后传入的。
适配器可以把某个类的接口转换成用户需要的另一个几口。主要有有三种,接口适配器模式、类适配器模式和对象适配器模式。
其中接口适配器模式是最简单的,我们从接口适配器模式说起。
接口适配器模式很常见,比如说在很多时候某个接口中有大量的方法,但是我们在使用的时候不一定需要每一个方法。没必要全部都实现,那么我们就可以使用接口适配器模式引入一个抽象类实现该接口所有方法(空实现),当我们在需要使用这个接口方法的时候,只需要继承该抽象类,选择要实现的方法即可。
//该接口中又大量的方法
public interface IController {
void add();
void delete();
void update();
void qury();
}
//声明抽象类实现接口,并空实现所有方法
public abstract class SimpleController implements IController {
@Override
public void add() {
}
@Override
public void delete() {
}
@Override
public void update() {
}
@Override
public void qury() {
}
}
//只需要重写需要的方法即可,无需全部重写!
public class Record extends SimpleController {
@Override
public void add() {
super.add();
Log.i("---","------------> 添加一条记录");
}
}
类适配器模式是为了兼容原有类的方法,满足新需求的一种方式。换句话说就是可以在不修改源类的基础上,通过引入适配器类,对源类进行扩展。和装饰者模式有点像啊。装饰者模式也是对源类进行扩展的,只不过装饰者模式是通过针对接口抽象的方式实现的。这里是通过引入适配器类实现的。
//比如说已有源类Sourceable
public class Sourceable {
public void eat() {
Log.i("---", "--->源方法");
}
}
//现在要对源类Sourceable进行扩展,不光要有eat()方法了,我还要有play(),drink()等方法,怎么办呢?
//声明新的目标接口,该接口中含有源类和新需求的方法
public interface Targetable {
void eat();
void play();
void drink();
void sleep();
}
//声明适配器接口,继承源类并实现新的功能接口
public class ClassAdapter extends Sourceable implements Targetable {
@Override
public void play() {
}
@Override
public void drink() {
}
@Override
public void sleep() {
}
}
这样就兼容了源类中的eat方法,并扩展了新的需求。
对象适配器模式更像是类适配器模式的优化,因为如果按照类适配器的写法,由于java是单继承的,继承了该类就不能继承其他类了。有一定的局限性。而对象适配器模式呢,就是把适配器中对源类的操作,从继承关系,转换到了多态的方式实现。增加了扩展性。
public class ObjectAdapter implements Targetable {
private Sourceable mSourceable;
public ObjectAdapter(Sourceable mSourceable) {
this.mSourceable = mSourceable;
}
@Override
public void eat() {
mSourceable.equals();
}
@Override
public void play() {
}
@Override
public void drink() {
}
@Override
public void sleep() {
}
}
从这里看适配器模式和装饰者模式真的好像啊。
相同点:
1. 都是针对源类抽象出的接口
2. 都需要在中间类中传入源类对象,从而实现对源类方法的调用。
不同点:
适配器模式中,源类并没有实现抽象出的接口。是通过传入源类对象实现调用的。
而装饰者模式,源类需要实现抽象出的接口,在装饰类中传入被装饰类对象.