裝飾者模式(
Decorator Pattern
)是在不必改變原類文件和使用繼承的情況下堕伪,動態(tài)地擴展一個對象的功能,它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象。
裝飾者是代替繼承擴展功能另外一種方式,裝飾者模式遵循了多用組合少用繼承和對擴展開放對修改關(guān)閉的設(shè)計原則蚌成。
這一次用兩個例子來說明,一個關(guān)于車的凛捏,一個是項目實際中經(jīng)常用到的驗證問題。
假如你有一天醒了芹缔,起來發(fā)現(xiàn)有人送了一輛法拉利488坯癣,那么有個性的你怎么會只滿足車輛本身的性能和外觀,這時候你會想著按照自己的意愿修改也就是改造最欠。改了顏色示罗、花紋、排氣管等等要再改就比較麻煩了芝硬,既然是做夢蚜点,索性就做一點智能的東西,隨意修改改裝配件拌阴,還不用破壞車子本來的樣子绍绘,接下來就用裝飾者模式來做做夢~
類圖
類圖不是目的,只是方便理解
[圖片上傳失敗...(image-bfea17-1527174192660)]
前面提到了裝飾者模式是創(chuàng)建包裝對象,所以裝飾者模式里面的類不管是具體的組件還是裝飾者組件都是實現(xiàn)同樣的接口或者類陪拘。
Component
是裝飾的裝飾接口厂镇,ConcreteComponent
是具體的裝飾組件,DecoratorComponent
是裝飾者組件左刽,裝飾者模式的核心就是用裝飾者去裝飾具體的組件捺信,同樣的一個裝飾者組件也可以當(dāng)成被裝飾的具體組件。
在這個類圖中欠痴,可以用DecorateComponentA
或者DecorateComponentB
去裝飾ConcreteComponent
迄靠、DecorateComponentA
、DecorateComponentB
喇辽,你要是非常喪心病狂也可以用DecorateComponentA
裝飾自己掌挚。
接下來實現(xiàn)自己的汽車智能改裝
實例
現(xiàn)在我們用之前的知識梳理一下如何實現(xiàn)一個汽車的智能改裝。
首先茵臭,需要一個Component
也就是組件接口疫诽,因為我們是定義的改裝所以定義成IUpdate
,里面有一個叫refit()
改裝的行為:
/**
* 改裝,裝飾者的裝飾接口
* Created by Carlton on 2016/11/2.
*/
interface IUpdate
{
/**
* 改裝
*/
fun refit() : String
}
有了裝飾者接口旦委,現(xiàn)在還需要一個被裝飾的具體裝飾組件奇徒,要不然妝化那么好,穿那么漂亮給誰看呢缨硝?女為悅己者容摩钙,所以得先要有一個服務(wù)的對象ConcreteComponent
,這里定義成Car
查辩,這是別人送給你的4s店原裝無改動的法拉利 488:
/**
* 汽車, 被裝飾的組件
* Created by Carlton on 2016/11/2.
*/
class Car(var name: String) : IUpdate
{
override fun refit(): String
{
return ""
}
override fun toString(): String
{
return "Car(name='$name')"
}
}
現(xiàn)在有了具體的目標胖笛,接下來就需要很多額外的配件來改裝,我們想改變汽車的顏色宜岛、速度长踊、是否需要噴火,所以先定義4個類:RedColor萍倡、BlueColor身弊、Speed、Fire
:
/**
* 紅色改裝
* Created by Carlton on 2016/11/2.
*/
class RedColor(update: IUpdate) : DecoratorUpdate(update)
{
override fun refit(): String
{
return "${update.refit()} - 紅色的"
}
}
/**
* 藍色改裝
* Created by Carlton on 2016/11/2.
*/
class BlueColor(update: IUpdate) : DecoratorUpdate(update)
{
override fun refit(): String
{
return "${update.refit()} - 藍色的"
}
}
/**
* 速度裝飾者
* Created by Carlton on 2016/11/2.
*/
class Speed(update: IUpdate) : DecoratorUpdate(update)
{
override fun refit(): String
{
return "${update.refit()} - 速度提升一倍"
}
}
/**
* 能噴火的裝飾者
* Created by Carlton on 2016/11/2.
*/
class Fire(update: IUpdate) : DecoratorUpdate(update)
{
override fun refit(): String
{
return "${update.refit()} - 能噴火"
}
fun fire(): String
{
return "${refit()}:氮氣"
}
}
注意這里的
Fire
類列敲,里面多了一個方法叫fire()
阱佛,功能是噴氮氣。這是裝飾者模式擴展的汽車原來的行為戴而,可以給每一個裝飾者組件添加多余的功能凑术。
現(xiàn)在,汽車和原材料都準備好了所意,那么:
- 我想要一輛紅色淮逊、能噴氮氣催首、速度極快的車車
- 我想要一輛藍色、速度極快的車車
- 我想要一輛藍色壮莹、速度翅帜、速度的車車
//紅色、氮氣命满、速度提升一倍的Ferrari
val car:Car = Car("Ferrari 488")
val refit = Fire(Speed(RedColor(car))).fire()
println("$car $refit")
// Car(name='Ferrari 488') - 紅色的 - 速度提升一倍 - 能噴火:氮氣
// 藍色涝滴、速度極快的
val car:Car = Car("Ferrari 488")
val refit = Speed(BlueColor(car)).refit()
println("$car $refit")
// Car(name='Ferrari 488') - 藍色的 - 速度提升一倍
// 藍色、速度胶台、速度
val car:Car = Car("Ferrari 488")
val refit = Speed(Speed(BlueColor(car))).refit()
println("$car $refit")
// Car(name='Ferrari 488') - 藍色的 - 速度提升一倍 - 速度提升一倍
既然如此歼疮,那我買一輛自行車也能改裝嘛,所以我們現(xiàn)在新增一個具體的裝飾者組件Bike
自行車:
/**
* 自行車, 具體的裝飾者組件
* Created by Carlton on 2016/11/2.
*/
class Bike(var name: String) : IUpdate
{
override fun refit(): String
{
return ""
}
override fun toString(): String
{
return "Bike(name='$name')"
}
}
一輛藍色诈唬、能噴火韩脏、速度提升的自行車由此誕生:
val bike:Bike = Bike("鳳凰牌自行車")
val refitBike = Speed(Fire(BlueColor(bike))).refit()
println("$bike $refitBike")
// Bike(name='鳳凰牌自行車') - 藍色的 - 能噴火 - 速度提升一倍
例子的類圖:
類圖不是目的,只是方便理解
[圖片上傳失敗...(image-6c50e1-1527174192660)]
在這個例子中Car
和Bike
都是具體的裝飾者組件铸磅,而繼承自DecoratorUpdate
的都是裝飾者組件赡矢,可以自由擴展其他功能,比如Fire
中的fire()
阅仔,裝飾者組件本身也可以被裝飾吹散。
項目應(yīng)用
以下代碼為Java實現(xiàn)
我們經(jīng)常會遇到一個需求就是對前端的字段驗證,比如字段是不是為空八酒,手機號格式是否正確等等空民,如果對字段的判斷需要新增規(guī)則用裝飾者模式就能愉快的解決這個問題,接下來實現(xiàn)一個驗證的裝飾者羞迷,對字段的驗證通過裝飾者來實現(xiàn)界轩。我們實現(xiàn)一個正則表達式的裝飾者和一個函數(shù)方法的裝飾者(驗證字段可以自定義規(guī)則)。
首先衔瓮,我們需要一個裝飾者的接口:
/**
* 參數(shù)驗證接口
* Created by Carlton on 2016/10/31.
*/
public interface IParamValidate<T>
{
/**
* 驗證是否通過浊猾,先驗證自身的條件再驗證裝飾者。先驗證被裝飾者热鞍,在驗證自身的條件与殃。
*
* @param value 被驗證的字段
*
* @return 如果是true驗證通過,如果是false驗證失敗碍现。如果value==null,返回false
*/
public boolean validate(T value);
}
然后米奸,實現(xiàn)一個具體的裝飾對象EmptyValidate
:
/**
* 字段為空的驗證昼接,也是裝飾者中的被裝飾的對象.
* 如果數(shù)據(jù)類型是null 返回false,如果數(shù)據(jù)類型是CharSequence則使用{@link TextUtils#isEmpty(CharSequence)}判斷字符是否為空悴晰。
* 所有的true代表驗證通過慢睡,false代表驗證不通過逐工。
* Created by Carlton on 2016/11/1.
*/
public class EmptyValidate<T> implements IParamValidate<T>
{
@Override
public boolean validate(T value)
{
return value != null && (!(value instanceof CharSequence) || !TextUtils.isEmpty((CharSequence) value));
}
}
有了具體的裝飾對象,現(xiàn)在我們實現(xiàn)一個正則表達式的驗證和一個函數(shù)驗證的裝飾者:
/**
* 驗證的接口漂辐,用于擴展自定義的驗證方式泪喊。
* 實現(xiàn)的返回必須遵循下面的規(guī)則:
* <ul>
* <li>
* 1、如果value==null則返回false髓涯。
* </li>
* <li>
* 2袒啼、如果返回true則代表驗證通過,如果返回false則代表驗證失敗纬纪。
* </li>
* </ul>
* Created by Carlton on 2016/11/1.
*/
public interface IValidator<T>
{
/**
* 自定義的驗證接口方法
*
* @param value 被驗證的值
*
* @return true是驗證通過蚓再,false驗證失敗
*/
public boolean validate(T value);
}
/**
* 函數(shù)驗證,通過一個自定義的函數(shù)來實現(xiàn)驗證包各。如果Validator==null 返回自身驗證返回true摘仅。
* Created by Carlton on 2016/11/1.
*/
public class FunctionValidate<T> extends ValidateDecorator<T>
{
private IValidator<T> mValidator;
public FunctionValidate(IParamValidate<T> validate, IValidator<T> validator)
{
super(validate);
mValidator = validator;
}
private boolean selfValidate(T value)
{
return mValidator == null || mValidator.validate(value);
}
@Override
public boolean validate(T value)
{
return getValidate().validate(value) && selfValidate(value);
}
}
/**
* 正則表達式驗證。使用一個正則表達是來匹配驗證问畅。
* 如果value == null返回false娃属。
* 如果是true驗證通過,如果是false驗證不通過护姆。
* Created by Carlton on 2016/11/1.
*/
public class RexValidate extends ValidateDecorator<CharSequence>
{
private String mPatternString = "\\.";
public RexValidate(String patternString, IParamValidate<CharSequence> validate)
{
super(validate);
mPatternString = patternString;
}
private boolean selfValidate(CharSequence value)
{
if(value == null)
{
return false;
}
Pattern pattern = Pattern.compile(mPatternString);
Matcher matcher = pattern.matcher(value);
return matcher.find();
}
@Override
public boolean validate(CharSequence value)
{
return getValidate().validate(value) && selfValidate(value);
}
}
IValidator是函數(shù)驗證的接口矾端,然后就是怎么使用。
- 驗證一個字段是否是空签则、是否匹配正則“abc”须床、首字母是否是C
實現(xiàn)代碼:
public static void main(String[] args)
{
String validateValue = "這是被驗證的字段";
// 驗證是否是空
EmptyValidate<CharSequence> emptyValidate = new EmptyValidate<>();
// 驗證是否通過正則表達式'abc'
RexValidate rxValidate = new RexValidate("abc", emptyValidate);
// 驗證自定義函數(shù),首字母是否是C
boolean result = new FunctionValidate<>(rxValidate, new IValidator<CharSequence>() {
@Override
public boolean validate(CharSequence value)
{
return !(value == null || value.length() > 0) && value.charAt(0) == 'C';
}
}).validate(validateValue);
System.out.println(result);
}
同樣的這些規(guī)則可以任意組合渐裂,自己也能實現(xiàn)其他的驗證規(guī)則豺旬,比如可以把手機號驗證直接實現(xiàn)成一個裝飾者。
總結(jié)
裝飾者模式解決的問題是動態(tài)的將責(zé)任附加到對象上柒凉,若要擴展功能族阅,裝飾者提供了比繼承更有彈性的替代方案。萬物都是平衡的膝捞,裝飾者也有它自己的缺點坦刀,主要就是會產(chǎn)生很多類,在使用的時候不要無腦套用蔬咬,需要自己權(quán)衡是否合適鲤遥。另外java中的I/O Stream是用裝飾者實現(xiàn)的,本來想擴展一個IO Stream對象林艘,想來比較畫蛇添足盖奈,有興趣的可以去了解一下。
不登高山狐援,不知天之高也钢坦;不臨深溪究孕,不知地之厚也
感謝指點、交流爹凹、喜歡