Friday, January 29, 2021

Android Component Pattern

 

创建抽象组件角色
这里就是一个网站的抽象页面元素:

============================================================

    public abstract class PageElement {//页面
        protected List<PageElement> mPageElements = new ArrayList<>();//用来保存页面元素
        private String name;

        public PageElement(String name) {
            this.name = name;
        }

        public abstract void addPageElement(PageElement pageElement);//添加栏目或者具体内容

        public abstract void rmPageElement(PageElement pageElement);//删除栏目或者具体内容
        
        public abstract void clear();//清空所有元素

        public abstract void print(String placeholder);//打印页面结构
        
        public String getName() {
            return name;
        }
    }

============================================================

创建叶子节点
叶子节点继承了抽象组件角色,但是由于没有分支,所以一些添加删除操作是实现不了的。叶子节点都是一些具体的内容,比如具体的音乐内容、视屏内容等等。

============================================================
    public class Content extends PageElement {//具体内容

        public Content(String name) {
            super(name);
        }

        @Override
        public void addPageElement(PageElement pageElement) {
            throw new UnsupportedOperationException("不支持此操作");
        }

        @Override
        public void rmPageElement(PageElement pageElement) {
            throw new UnsupportedOperationException("不支持此操作");
        }

        @Override
        public void clear() {
            throw new UnsupportedOperationException("不支持此操作");
        }

        @Override
        public void print(String placeholder) {
            System.out.println(placeholder + "──" + getName());
        }
        
    }
============================================================

创建树枝节点
树枝节点能够删除添加叶子或树枝。

============================================================
     public class Column extends PageElement {//栏目

        public Column(String name) {
            super(name);
        }

        @Override
        public void addPageElement(PageElement pageElement) {
            mPageElements.add(pageElement);
        }

        @Override
        public void rmPageElement(PageElement pageElement) {
            mPageElements.remove(pageElement);
        }

        @Override
        public void clear() {
            mPageElements.clear();
        }
        
        /**
         * @param placeholder 占位符
         */
        @Override
        public void print(String placeholder) {
            //利用递归来打印文件夹结构
            System.out.println(placeholder + "└──" + getName());
            Iterator<PageElement> i = mPageElements.iterator();
            while (i.hasNext()) {
                PageElement pageElement = i.next();
                pageElement.print(placeholder + "   ");
            }
        }

    }
============================================================

客户端测试:
============================================================
    public void test() {
        //创建网站根页面 root
        PageElement root = new Column("网站页面");
        //网站页面添加两个栏目:音乐,视屏;以及一个广告内容。
        PageElement music = new Column("音乐");
        PageElement video = new Column("视屏");
        PageElement ad = new Content("广告");
        root.addPageElement(music);
        root.addPageElement(video);
        root.addPageElement(ad);

        //音乐栏目添加两个子栏目:国语,粤语
        PageElement chineseMusic = new Column("国语");
        PageElement cantoneseMusic = new Column("粤语");
        music.addPageElement(chineseMusic);
        music.addPageElement(cantoneseMusic);

        //国语,粤语栏目添加具体内容
        chineseMusic.addPageElement(new Content("十年.mp3"));
        cantoneseMusic.addPageElement(new Content("明年今日.mp3"));

        //视频栏目添加具体内容
        video.addPageElement(new Content("唐伯虎点秋香.avi"));

        //打印整个页面的内容
        root.print("");
    }
============================================================

其他说明:
上面的例子可以看到叶子节点其实并不需要添加删除等方法,但由于叶子节点实际上是依赖了抽象组件角色。一方面,这遵循了依赖倒置原则——依赖抽象,而不依赖具体实现

同时,也保证了叶子节点跟树枝节点具体相同的结构,即他们具有同样的方法接口,能够让客户端以一致的方式去处理单个对象和组合对象。

但另一方,这违反了单一职责原则接口隔离原则,让 叶子节点继承了它本不应该有的方法,并且不太优雅的抛出了 UnsupportedOperationException 。这实际叫透明的组合模式

安全的组合模式

另外一种组合模式叫安全的组合模式。这种模式客户端在使用的时候必须依赖具体的实现,这违反了依赖倒置原则,但遵循了单一职责原则接口隔离原则

实现

============================================================
    public abstract class PageElement {//页面
        private String name;

        public PageElement(String name) {
            this.name = name;
        }

        //抽象组件角色去掉增删等接口

        public abstract void print(String placeholder);

        public String getName() {
            return name;
        }
    }
    
    public class Content extends PageElement {//具体内容,只专注自己的职责

        public Content(String name) {
            super(name);
        }
        
        @Override
        public void print(String placeholder) {
            System.out.println(placeholder + "──" + getName());
        }
    }
    
    public class Column extends PageElement {//栏目
        private List<PageElement> mPageElements = new ArrayList<>();//用来保存页面元素

        public Column(String name) {
            super(name);
        }

        public void addPageElement(PageElement pageElement) {
            mPageElements.add(pageElement);
        }

        public void rmPageElement(PageElement pageElement) {
            mPageElements.remove(pageElement);
        }

        public void clear() {
            mPageElements.clear();
        }

        @Override
        public void print(String placeholder) {
            System.out.println(placeholder + "└──" + getName());
            Iterator<PageElement> i = mPageElements.iterator();
            while (i.hasNext()) {
                PageElement pageElement = i.next();
                pageElement.print(placeholder + "   ");
            }
        }

    }
    
    public void test() {//客户端测试方法
        //依赖具体的实现类Column
        Column root = new Column("网站页面");
       
        Column music = new Column("音乐");
        Column video = new Column("视屏");
        PageElement ad = new Content("广告");
        root.addPageElement(music);
        root.addPageElement(video);
        root.addPageElement(ad);

        Column chineseMusic = new Column("国语");
        Column cantoneseMusic = new Column("粤语");
        music.addPageElement(chineseMusic);
        music.addPageElement(cantoneseMusic);

        chineseMusic.addPageElement(new Content("十年.mp3"));
        cantoneseMusic.addPageElement(new Content("明年今日.mp3"));

        video.addPageElement(new Content("唐伯虎点秋香.avi"));

        root.print("");
    }
============================================================

对比

安全的组合模式将职责区分开来放在不同的接口中,这样一来,设计上就比较安全,也遵循了单一职责原则和接口隔离原则,但是也让客户端必须依赖于具体的实现;透明的组合模式,以违反单一职责原则和接口隔离原则来换取透明性,但遵循依赖倒置原则,客户端可以直接依赖于抽象组件即可,将叶子和树枝一视同仁,也就是说,一个元素究竟是枝干节点还是叶子节点,对客户端是透明的。
  
一方面,我们写代码时应该遵循各种设计原则,但实际上,有些设计模式原则在使用时会发生冲突,这就需要我们根据实际情况去衡量做出取舍,适合自己的才是最好的。


Android中的源码分析

Android源码中,ViewGroup 和 View就是典型的组合模式。

View类

View相当与叶子节点,里面没有添加删除 View 等操作。

============================================================

    public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
        //具体代码略
    }

============================================================

ViewGroup类

ViewGroup实际上是View的子类,同时ViewGroup中实现了添加删除View等操作,因此可以作为容器存放view。

============================================================
    public abstract class ViewGroup extends android.view.View implements ViewParent, ViewManager {//继承View
        @Override
        public void addView(View child, android.view.ViewGroup.LayoutParams params) {//添加view
            //具体实现代码略
        }

        @Override
        public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {//更新view 
            //具体实现代码略
        }

        @Override
        public void removeView(View view) {//移除view
            //具体实现代码略
        }

        //其他代码略
    }
============================================================


ViewManager接口

实际上ViewGroup中的了添加删除View是实现了ViewManager接口中的方法:

============================================================
public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
============================================================


9.4 其他

可以看出ViewGroup 和 View 使用的是安全的组合模式,而不是透明的组合模式。

Reference:

https://www.jianshu.com/p/0580301d141d

No comments:

Post a Comment

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能