Thursday, February 4, 2021

Android Visitor Pattern

 

1.定义

封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

2.介绍

  • 访问者模式属于行为型模式。
  • 访问者模式是一种将数据结构数据操作分离的设计模式。
  • 访问者模式比较复杂,而且实际使用的地方并不多。
  • 访问者模式适用于数据结构稳定的元素操作上,一旦数据结构易变,则不适用。

5. 应用场景

  • 对象结构比较稳定,很少改变,但是经常需要在此对象结构上定义新的操作行为时。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

6. 优点

  • 各种角色各司其职,符合单一职责原则。
  • 原有的类上新增操作只需实现一个具体访问者就可以,不 必修改整个类层次,符合开闭原则。
  • 良好的扩展性,新增访问操作变得简单。
  • 数据操作和数据结构的解耦。

7. 缺点

  • 具体元素对访问者公布了实现细节,破坏了类的封装性,违反了迪米特原则。
  • 违反了依赖倒置原则,为了达到区别对待依赖了具体而不是抽象。
  • 具体元素修改的成本太大。
  • 新增具体元素困难,需要在抽象访问者角色中增加一个新的抽象操作,违反了开闭原则。


Android中运用访问者模式,其实主要是在编译期注解中,编译期注解核心原理依赖APT(Annotation Processing Tools),著名的开源库比如ButterKnife、Dagger、Retrofit都是基于APT。

Reference:

https://www.jianshu.com/p/279563870478

https://www.jianshu.com/p/62289b9bcfcb

Tmux index

用 tmux 保持遠端 ssh 連線

tmux关机永久保存会话

Tmux Cheat Sheet & Quick Reference

Run tmux



Run tmux

echo "create 2k11 Android  Ref session"

ROOT_PATH=`pwd`

CONN_PATH="src/"

BASE_PATH=${ROOT_PATH}/${CONN_PATH}

i=0

tmuxname[$((i++))]="build1"

tmuxname[$((i++))]="sync1"

tmuxname[$((i++))]="phone1"

tmuxname[$((i++))]="phone2"

tmuxname[$((i++))]="app1"

tmuxname[$((i++))]="app2"

tmuxname[$((i++))]="fmk"

tmuxname[$((i++))]="xml"


i=0

tmuxpath[$((i++))]=${BASE_PATH}

tmuxpath[$((i++))]=${BASE_PATH}

tmuxpath[$((i++))]=${BASE_PATH}/vendor/xxx

tmuxpath[$((i++))]=${BASE_PATH}/vendor/xxx

tmuxpath[$((i++))]=${BASE_PATH}/vendor/xxx

tmuxpath[$((i++))]=${BASE_PATH}/vendor/xxx

tmuxpath[$((i++))]=${BASE_PATH}/frameworks

tmuxpath[$((i++))]=${BASE_PATH}/xxx


fnum=${i}


pushd ${BASE_PATH}

  for ((i=0; i<${fnum}; i++))

  do

    tmux new -s ${tmuxname[$i]} -n add_1 -d "cd ${BASE_PATH};bash -i"

  done

popd


Wednesday, February 3, 2021

Android Memento Pattern

1.定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到先前保存的状态。

2.介绍

  • 备忘录模式属于行为型模式。
  • 备忘录模式比较适合用于功能复杂,但是需要维护和纪录历史的地方,或者是需要保存一个或者多个属性的地方;在未来某个时刻需要时,将其还原到原来纪录的状态。
角色说明:
  • Originator(发起人角色):负责创建一个备忘录(Memoto),能够记录内部状态,以及恢复原来记录的状态。并且能够决定哪些状态是需要备忘的。
  • Memoto(备忘录角色):将发起人(Originator)对象的内部状态存储起来;并且可以防止发起人(Originator)之外的对象访问备忘录(Memoto)。
  • Caretaker(负责人角色):负责保存备忘录(Memoto),不能对备忘录(Memoto)的内容进行操作和访问,只能将备忘录传递给其他对象。

5. 应用场景

  • 需要保存对象的某一时刻的状态时

6. 优点

  • 能够让状态回滚到某一时刻的状态
  • 实现了状态保存对象的封装,用户无需关心其实现细节。

7. 缺点

  • 要保存的对象如果成员变量过多的话,资源消耗也会相应增多。

8. Android中的源码分析

Android中的Activity就提供了状态保存机制来保证Activity在被系统回收后能够恢复当前Activity的数据。这一机制实际上就是onSaveInstanceStateonRestoreInstanceStateonSaveInstanceState就是用来保存当前Activity的状态,onRestoreInstanceState则是用来恢复Activity的状态。

8.1 onSaveInstanceState 源码

=========================================================
    protected void onSaveInstanceState(Bundle outState) {//保存各种状态

        //1.保存Activity对应Window的状态信息
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        //2.如果存在Fragments,则保存所有Fragments的状态信息
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }

        //3.如果设置了ActivityLifecycleCallbacks回调,那么会调用ActivityLifecycleCallbacks的onSaveInstanceState来进行保存状态信息
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }
=========================================================

8.2 onRestoreInstanceState 源码

=========================================================
   protected void onRestoreInstanceState(Bundle savedInstanceState) {
        if (mWindow != null) {
            //获取保存过的window状态信息
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            //如果存在状态信息,则window进行恢复
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }
=========================================================

Tuesday, February 2, 2021

Android Iterator Pattern

 

1.定义

提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

2.介绍

  • 迭代器模式属于行为型模式。
  • 迭代器(Iterator)模式,又叫做游标(Cursor)模式。
  • Java中的Map、List等等容器,都使用到了迭代器模式。
角色说明:
  • Iterator(迭代器接口):负责定义、访问和遍历元素的接口。
  • ConcreteIterator(具体迭代器类):实现迭代器接口。
  • Aggregate(容器接口):定义容器的基本功能以及提供创建迭代器的接口。
  • ConcreteAggregate(具体容器类):实现容器接口中的功能。
  • Client(客户端类):即要使用迭代器模式的地方。

4.实现

继续以快递举例子,货架上有很多快递包,取快递时我们报出手机号,然后快递小哥就遍历货架上所有的快递信息,直到找出快递包为止:

4.1 创建迭代器接口
===================================================================
    public interface Iterator {

        boolean hasNext();    //是否存在下一条记录

        Object next();        //返回当前记录并移到下一条记录
    }
===================================================================

4.2 创建容器接口
定义容器的基本功能以及提供创建迭代器的接口:

===================================================================
     public interface Aggregate {
      
        int size();//容器大小

        String get(int location);//获取获取中指定位置的号码

        void add(String tel);//添加号码到容器中

        void remove(String tel);//从容器中移除号码
        
        Iterator iterator();//返回容器的迭代器
    }
===================================================================

4.3 创建具体迭代器类
实现迭代器接口:

===================================================================
    //快递迭代类
    public class DeliveryIterator implements Iterator {
        private Aggregate aggregate;//容器对象
        private int index;//当前索引

        public DeliveryIterator(Aggregate aggregate) {
            this.aggregate = aggregate;//初始化容器对象
        }

        @Override
        public boolean hasNext() {//是否存在下一条记录
            if (index < aggregate.size()) {
                return true;
            } else {
                return false;
            }
        }

        @Override
        public Object next() {//返回当前记录并移到下一条记录
            return aggregate.get(index++);
        }
    }
===================================================================

4.4 创建具体容器类
实现容器接口中的功能:

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

    //快递容器类
    public class DeliveryAggregate implements Aggregate {
        private List<String> list = new ArrayList<>();//内部使用list来存储数据

        public int size() {//容器大小
            return list.size();
        }

        public String get(int location) {
            return list.get(location);
        }

        public void add(String tel) {
            list.add(tel);
        }

        public void remove(String tel) {
            list.remove(tel);
        }

        @Override
        public Iterator iterator() {////返回迭代器
            return new DeliveryIterator(this);
        }
    }
===================================================================

输出结果:
===================================================================
当前号码为:1111
当前号码为:2222
当前号码为:3333
当前号码为:9527
后面没有了
===================================================================

5. 应用场景

  • 遍历一个容器对象时。
  • 实际我们开发中很少使用到迭代器模式。虽然不怎么用得到,但是了解其原理能够让我们在看到相关的源码(如Java中的Map、List等等容器)时能够更容易了解源码的相关思想。

6. 优点

  • 可以支持以不同的方式去遍历容器对象,如顺序遍历,逆序遍历等等。
  • 符合单一职责原则,容器类与遍历算法的分离,不同类负责不同的功能。

7. 缺点

  • 会产生多余的对象,消耗内存。
  • 会增多类文件。
  • 遍历过程是一个单向且不可逆的遍历。
  • 遍历过程需要注意容器是否改变,若期间改变了,可能会抛出异常。

8. Android中的源码分析

除了Java中的Map、List等有用到迭代器模式之外,Android中使用数据库查询时返回的Cursor游标对象,实际上就是使用了迭代器模式来实现,首先先让我们来看下怎么使用Cursor:

8.1 使用Cursor

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

        Cursor cursor = sqLiteDatabase.query("table", null, null, null, null, null, null);//各种参数请自行查询用法,这里就不说明了
        if (cursor.moveToFirst()) {//游标指向第一行
            do {
                cursor.getInt(0);//获得第一列数据
                cursor.getString(1);//获得第二列数据
            } while (cursor.moveToNext());//移到下一行
        }

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

8.2 Cursor源码

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

//Cursor是一个接口,实际上就是迭代器接口
public interface Cursor extends Closeable {
    //其他代码略
    boolean moveToFirst();
    int getInt(int columnIndex);
    String getString(int columnIndex);
    boolean moveToNext();
}

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

Android Template Method

 

5. 应用场景

  • 一次性实现算法的执行顺序和固定不变部分,可变部分则交由子类来实现。
  • 多个子类中拥有相同的行为时,可以将其抽取出来放在父类中,避免重复的代码。
  • 使用钩子方法来让子类决定父类的某个步骤是否执行,实现子类对父类的反向控制。
  • 控制子类扩展。模板方法只在特定点调用钩子方法,这样就只允许在这些点进行扩展。

6. 优点

  • 提高代码复用性,去除子类中的重复代码。
  • 提高扩展性,不同实现细节放到不同子类中,易于增加新行为。

7. 缺点

每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。


Android中的源码分析

Android中 View 的 draw 方法就是使用了模板方法模式:

View的draw方法

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

public class View{
    //钩子方法,空实现
    protected void onDraw(Canvas canvas) {
    }
    //钩子方法,空实现
    protected void dispatchDraw(Canvas canvas) {
    }
    //绘制方法,定义绘制流程
    public void draw(Canvas canvas) {
       //其他代码略

        /*
         *  绘制流程如下:
         *
         *      1. 绘制view背景
         *      2. 如果有需要,就保存图层
         *      3. 绘制view内容
         *      4. 绘制子View
         *      5. 如果有必要,绘制渐变框和恢复图层
         *      6. 绘制装饰(滑动条等)
         */

        if (!dirtyOpaque) {
            drawBackground(canvas);//步骤1. 绘制view背景
        }

        // 如果可能的话跳过第2步和第5步(常见情况)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            if (!dirtyOpaque) onDraw(canvas);//步骤3. 绘制view内容

            dispatchDraw(canvas);//步骤4. 绘制子View

            // 覆盖一部分内容,绘制前景
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            onDrawForeground(canvas); //步骤6. 绘制装饰(滑动条等)

            return;
        }
}

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

说明

  • Viewdraw()方法中定义了一整套的绘制流程,这个流程是固定的,所有的Android中的View都是按照这个流程来绘制的。其中drawBackground()这个方法在View类中是实现了具体过程的,而onDraw()方法和dispatchDraw()方法在View中都是空实现,即都是钩子方法。不同的子类通过重写这些空实现来实现自身不同的绘制效果。
  • 具体的View,像TextView这些单一的View,就会重写onDraw()方法,由于TextView没有子View,所以dispatchDraw()还是空实现;而ViewGroup类含有子View,需要遍历子View并绘制,因此需要重写onDraw()dispatchDraw()
  • 所以,我们自定义View时必须且只需重写onDraw();自定义ViewGroup时则需要重写onDraw()dispatchDraw()

  • n8n index

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