迭代器模式实现与研究 - bei1999/work GitHub Wiki

定义

提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

场景模拟

迭代器模式又称为游标(cursor)模式,源自于容器类的访问,比如java 中的list ,map 数组等,将遍历的方法封装到容器中,方式容器类的内部细节暴露,因此产生了迭代器模式

实现

说到游标,首先想到的是android 中的数据库中的cursor,下面是通过ContentProvider 调用数据库操作例子来纵观整个过程:

/**
 * author : lzb
 * e-mail : 
 * time   : 2017/12/20
 * desc   : 数据库管理类
 * version: 1.0
 */
public class DBHelper extends SQLiteOpenHelper {


    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE table_fav (_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,age TEXT)");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS table_fav");
        onCreate(db);

    }
}

/**
 * author : lzb
 * e-mail :
 * time   : 2017/12/20
 * desc   : 对外提供数据接口类
 * version: 1.0
 */
public class DataProvider extends ContentProvider {

    private DBHelper mDBHelper;
    private static final String DB_NAME = "favor.db";
    private static final String DB_TABLE = "table_fav";
    private static final int DB_VERSION = 1;
    private SQLiteDatabase db;

    @Override
    public boolean onCreate() {
        mDBHelper = new DBHelper(getContext(), DB_NAME, null, DB_VERSION);
        db = mDBHelper.getWritableDatabase();
        if (db == null) {
            return false;
        } else {
            return true;
        }
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//        SQLiteDatabase db = mDBHelper.getReadableDatabase();
        return db.query(DB_TABLE, projection, null, null, null, null, null);
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long id = db.insert(DB_TABLE,null,values);
        if (id > 0) {
            Uri newUri = ContentUris.withAppendedId(Favor.CONTENT_URI,id);
            getContext().getContentResolver().notifyChange(newUri,null);
            return newUri;
        }
        throw  new SQLException("failed to insert row into" +uri);
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

/**
 * author : lzb
 * e-mail :
 * time   : 2017/12/21
 * desc   : 常量
 * version: 1.0
 */
public class Favor {
    public static final String AUTHORITY = "com.bei.test.db.DataProvider";
    public static final String PATH = "table_fav";
    public static final String CONTENT_URL_STRING = "content://" +AUTHORITY +"/"+PATH;
    public static final Uri CONTENT_URI = Uri.parse(CONTENT_URL_STRING);
    public static final String KEY_NAME = "name";
    public static final String KEY_AGE = "age";
}


/**
 * author : lzb
 * e-mail : [email protected]
 * time   : 2017/12/20
 * desc   : 测试类
 * version: 1.0
 */
public class ContentResolverDemoActivity extends Activity {
    private static final String[] PROJECTION = new String[]{"name", "age"};
    @Bind(R.id.nameTv)
    EditText nameTv;
    @Bind(R.id.ageTv)
    EditText ageTv;
    @Bind(R.id.addBtn)
    Button addBtn;
    @Bind(R.id.queryBtn)
    Button queryBtn;
    @Bind(R.id.resultTv)
    TextView resultTv;
    @Bind(R.id.resultUrl)
    TextView resultUrl;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main09);
        ButterKnife.bind(this);


    }

    @OnClick({R.id.addBtn, R.id.queryBtn})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.addBtn:
                ContentValues contentValues = new ContentValues();
                contentValues.put(Favor.KEY_NAME, nameTv.getText().toString());
                contentValues.put(Favor.KEY_AGE, ageTv.getText().toString());

                Uri newUrl = this.getContentResolver().insert(Favor.CONTENT_URI, contentValues);

                resultUrl.setText("添加成功 ,URI:" + newUrl);

                break;
            case R.id.queryBtn:
                Cursor cursor = getContentResolver().query(Favor.CONTENT_URI, PROJECTION, null, null, null);
                String msg = "";
                cursor.moveToFirst();
                do {
                    msg += "name: " + cursor.getString(0);
                    msg += "age: " + cursor.getString(1)+"\n";


                } while (cursor.moveToNext());
                cursor.close();

                resultTv.setText(msg);

                break;
        }
    }
}

使用ContentProvider的时候,不要忘记AndroidManifest.xml中注册声明

        <provider
            android:authorities="com.bei.test.db.DataProvider"
            android:name=".db.DataProvider"/>

运行结果如下:

分析

使用了SQLiteOpenHelper 的query 方法查询,最终返回一个cursor 的游标对象,这其实就是一个迭代器,通过遍历游标的位置进行数据的遍历和查询。

总结

它的存在弱化了容器类与遍历算法之前的关系,很多语言比如java python等自身实现了迭代器功能,对于开发者而言很少自己去实现迭代器。