01-16-06 装饰器模式 - Context装饰链与InputStream装饰
01-16-06 装饰器模式 - Context装饰链与InputStream装饰装饰器模式是什么动态地给对象添加额外的功能比继承更灵活。核心要点装饰器与被装饰对象实现相同接口装饰器持有被装饰对象的引用可以动态叠加多个装饰器Android源码中的装饰器实现案例1Context装饰链// Context抽象类publicabstractclassContext{publicabstractResourcesgetResources();publicabstractPackageManagergetPackageManager();publicabstractvoidstartActivity(Intentintent);// ... 更多抽象方法}// ContextWrapper装饰器基类publicclassContextWrapperextendsContext{ContextmBase;// 被装饰的ContextpublicContextWrapper(Contextbase){mBasebase;}protectedvoidattachBaseContext(Contextbase){if(mBase!null){thrownewIllegalStateException(Base context already set);}mBasebase;}OverridepublicResourcesgetResources(){returnmBase.getResources();// 委托给mBase}OverridepublicvoidstartActivity(Intentintent){mBase.startActivity(intent);// 委托给mBase}}// ContextThemeWrapper增强主题功能publicclassContextThemeWrapperextendsContextWrapper{privateintmThemeResource;privateResources.ThememTheme;OverridepublicvoidsetTheme(intresid){mThemeResourceresid;initializeTheme();}OverridepublicResources.ThemegetTheme(){if(mTheme!null){returnmTheme;}mThemegetResources().newTheme();mTheme.applyStyle(mThemeResource,true);returnmTheme;}}// Activity最终的装饰器publicclassActivityextendsContextThemeWrapper{// 增强生命周期管理protectedvoidonCreate(BundlesavedInstanceState){// Activity特有逻辑}protectedvoidonResume(){// Activity特有逻辑}}装饰链ContextImpl (基础实现) ↓ ContextWrapper (装饰器基类) ↓ ContextThemeWrapper (主题装饰) ↓ Activity (生命周期装饰)使用示例// 自定义装饰器添加日志功能classLoggingContextWrapper(base:Context):ContextWrapper(base){overridefunstartActivity(intent:Intent){Log.d(LoggingContext,Starting activity:${intent.component})super.startActivity(intent)}overridefungetSystemService(name:String):Any?{Log.d(LoggingContext,Getting service:$name)returnsuper.getSystemService(name)}}// 使用classMainActivity:AppCompatActivity(){overridefunattachBaseContext(newBase:Context){super.attachBaseContext(LoggingContextWrapper(newBase))}}案例2InputStream装饰链// 抽象组件publicabstractclassInputStreamimplementsCloseable{publicabstractintread()throwsIOException;publicintread(byteb[])throwsIOException{returnread(b,0,b.length);}}// 具体组件publicclassFileInputStreamextendsInputStream{privateFileDescriptorfd;Overridepublicintread()throwsIOException{returnread0();// 读取文件}}// 装饰器基类publicclassFilterInputStreamextendsInputStream{protectedvolatileInputStreamin;// 被装饰对象protectedFilterInputStream(InputStreamin){this.inin;}Overridepublicintread()throwsIOException{returnin.read();// 委托}}// 具体装饰器1缓冲功能publicclassBufferedInputStreamextendsFilterInputStream{privatebytebuf[];// 缓冲区privateintpos;privateintcount;Overridepublicsynchronizedintread()throwsIOException{if(poscount){fill();// 填充缓冲区if(poscount)return-1;}returnbuf[pos]0xff;}privatevoidfill()throwsIOException{countin.read(buf,0,buf.length);pos0;}}// 具体装饰器2数据转换publicclassDataInputStreamextendsFilterInputStream{Overridepublicfinalintread(byteb[])throwsIOException{returnin.read(b,0,b.length);}publicfinalintreadInt()throwsIOException{intch1in.read();intch2in.read();intch3in.read();intch4in.read();return((ch124)(ch216)(ch38)(ch40));}publicfinalStringreadUTF()throwsIOException{returnDataInputStream.readUTF(this);}}装饰链叠加// 叠加多个装饰器valfisFileInputStream(/sdcard/file.txt)valbisBufferedInputStream(fis)// 添加缓冲valdisDataInputStream(bis)// 添加数据转换// 读取intvalvaluedis.readInt()// 关闭会层层关闭dis.close()实战应用场景1网络请求装饰器// 基础接口interfaceHttpClient{suspendfunrequest(url:String):Response}// 具体实现classOkHttpClientImpl:HttpClient{overridesuspendfunrequest(url:String):Response{// OkHttp请求returnResponse(200,OK)}}// 装饰器基类abstractclassHttpClientDecorator(privatevalclient:HttpClient):HttpClient{overridesuspendfunrequest(url:String)client.request(url)}// 日志装饰器classLoggingHttpClient(client:HttpClient):HttpClientDecorator(client){overridesuspendfunrequest(url:String):Response{Log.d(HTTP,Request:$url)valstartSystem.currentTimeMillis()valresponsesuper.request(url)valcostSystem.currentTimeMillis()-start Log.d(HTTP,Response:${response.code}(${cost}ms))returnresponse}}// 缓存装饰器classCachingHttpClient(client:HttpClient,privatevalcache:Cache):HttpClientDecorator(client){overridesuspendfunrequest(url:String):Response{cache[url]?.let{returnit}valresponsesuper.request(url)cache[url]responsereturnresponse}}// 重试装饰器classRetryHttpClient(client:HttpClient,privatevalmaxRetries:Int3):HttpClientDecorator(client){overridesuspendfunrequest(url:String):Response{repeat(maxRetries){attempt-try{returnsuper.request(url)}catch(e:Exception){if(attemptmaxRetries-1)throwedelay(1000*(attempt1))}}throwIllegalStateException(Should not reach here)}}// 使用叠加装饰器valclientRetryHttpClient(CachingHttpClient(LoggingHttpClient(OkHttpClientImpl()),cache),maxRetries3)valresponseclient.request(https://api.example.com/users)场景2TextView装饰器// 功能装饰器classTextViewDecorator(privatevaltextView:TextView){// 添加点击涟漪效果funwithRipple():TextViewDecorator{valoutValueTypedValue()textView.context.theme.resolveAttribute(android.R.attr.selectableItemBackground,outValue,true)textView.setBackgroundResource(outValue.resourceId)returnthis}// 添加图标funwithIcon(DrawableResiconRes:Int,position:IconPosition):TextViewDecorator{valiconContextCompat.getDrawable(textView.context,iconRes)icon?.setBounds(0,0,icon.intrinsicWidth,icon.intrinsicHeight)when(position){IconPosition.LEFT-textView.setCompoundDrawables(icon,null,null,null)IconPosition.RIGHT-textView.setCompoundDrawables(null,null,icon,null)IconPosition.TOP-textView.setCompoundDrawables(null,icon,null,null)IconPosition.BOTTOM-textView.setCompoundDrawables(null,null,null,icon)}returnthis}// 添加字体funwithFont(fontPath:String):TextViewDecorator{valtypefaceTypeface.createFromAsset(textView.context.assets,fontPath)textView.typefacetypefacereturnthis}// 添加描边funwithStroke(width:Float,color:Int):TextViewDecorator{textView.paint.strokeWidthwidth textView.paint.stylePaint.Style.STROKE textView.setTextColor(color)returnthis}}enumclassIconPosition{LEFT,RIGHT,TOP,BOTTOM}// 使用TextViewDecorator(textView).withRipple().withIcon(R.drawable.ic_star,IconPosition.LEFT).withFont(fonts/custom.ttf)装饰器 vs 继承// [未通过] 继承类爆炸classTextViewclassBoldTextView:TextView()classItalicTextView:TextView()classBoldItalicTextView:TextView()// 需要很多组合类classUnderlineTextView:TextView()classBoldUnderlineTextView:TextView()// ... 指数级增长// [通过] 装饰器灵活组合interfaceText{funrender():String}classPlainText(privatevalcontent:String):Text{overridefunrender()content}abstractclassTextDecorator(privatevaltext:Text):Text{overridefunrender()text.render()}classBoldDecorator(text:Text):TextDecorator(text){overridefunrender()b${super.render()}/b}classItalicDecorator(text:Text):TextDecorator(text){overridefunrender()i${super.render()}/i}classUnderlineDecorator(text:Text):TextDecorator(text){overridefunrender()u${super.render()}/u}// 灵活组合valtextUnderlineDecorator(ItalicDecorator(BoldDecorator(PlainText(Hello))))println(text.render())// uibHello/b/i/u常见问题Q1装饰器 vs 代理模式装饰器增强功能classLoggingDecorator(privatevalservice:Service):Service{overridefunoperation(){log(Before)service.operation()// 增强log(After)}}代理控制访问classProtectionProxy(privatevalservice:Service):Service{overridefunoperation(){if(hasPermission()){service.operation()// 控制}}}区别装饰器可以叠加多层增强功能代理通常只有一层控制访问Q2如何避免装饰链过长// [未通过] 装饰链过长难以维护valclientRetryDecorator(TimeoutDecorator(CacheDecorator(LoggingDecorator(MetricsDecorator(RateLimitDecorator(AuthDecorator(BaseClient())))))))// [通过] 使用Builder模式classHttpClientBuilder{privatevarclient:HttpClientBaseClient()funaddLogging()apply{clientLoggingDecorator(client)}funaddCache()apply{clientCacheDecorator(client)}funaddRetry(times:Int)apply{clientRetryDecorator(client,times)}funbuild()client}valclientHttpClientBuilder().addLogging().addCache().addRetry(3).build()总结核心思想动态添加功能比继承更灵活符合开闭原则对扩展开放对修改关闭可以叠加多个装饰器Android中的应用Context装饰链- Activity、Service继承链InputStream装饰链- BufferedInputStream、DataInputStreamDrawable装饰- LayerDrawable、InsetDrawable选择建议需要动态添加功能 → 装饰器模式功能组合很多 → 装饰器避免类爆炸需要控制访问 → 代理模式装饰器模式的核心价值是灵活扩展避免继承导致的类爆炸问题。