迭代器模式

网址链接:https://www.cnblogs.com/chenssy/p/3250409.html

 你项目组接到一个项目:对电视机的电视频道、电影和收音机菜单进行统一管理,建立一个统一的菜单管理界面,能够看到所有的电视界面、电影界面和收音机频道。你有三个手下:小李子、小杏子、小安子,他们分别就每个模块做开发工作,看他们都做了哪些工作。

      这是主菜单JavaBean,用于显示每个模块的菜单。

public class MenuItem {
    private String name;
    private String description;
    private int channe;
    
    public MenuItem(int channe,String name,String description){
        this.name = name;
        this.description = description;
        this.channe = channe;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getChanne() {
        return channe;
    }

    public void setChanne(int channe) {
        this.channe = channe;
    }
    
}

      小李子负责电视频道菜单的实现。他是使用List实现的,他认为这样就可以非常方便的扩展菜单。

public class TVChanneMenu implements TelevisionMenu{
    List<MenuItem> menuItems;
    
    /**
     * 构造函数完成初始化
     */
    public TVChanneMenu(){
        menuItems = new ArrayList<MenuItem>();
        addItem(1, "CCTV-1", "This is CCTV-1");
        addItem(2, "CCTV-2", "This is CCTV-2");
        addItem(3, "CCTV-3", "This is CCTV-3");
        addItem(4, "CCTV-4", "This is CCTV-4");
        addItem(5, "CCTV-5", "This is CCTV-5");
    }
    
    /**
     * @desc 将电视频道节目添加菜单集合中
     * @param channe  频道
     * @param name  名称
     * @param description  描述
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvMenuItem = new MenuItem(channe, name, description);
        menuItems.add(tvMenuItem);
    }

    public List<MenuItem> getMenuItems() {
        return menuItems;
    }

}

      小杏子负责电影菜单模块的开发。她是使用数组完成的,他认为数组的效率高些,而且能够控制菜单的长度。

public class FilmMenu implements TelevisionMenu{
    static final int MAX_ITEMS = 5;    //菜单最大长度
    MenuItem[] menuItems;
    int numberOfItems = 0;
    
    /**
     * 构造函数完成初始化
     */
    public FilmMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem(1, "绝世天劫", "这是布鲁斯威利斯主演的...");
        addItem(2, "达芬奇密码", "这是我最喜欢的电影之一...");
        addItem(3, "黑客帝国123", "不知道你能够看懂不???");
        addItem(4, "我的女友是机器人", "一部我不反感的经典爱情电影...");
        addItem(5, "肖申克的救赎", "自由,幸福,离你有多远");
    }
    
    /**
     * @desc 将电影解决添加到菜单项中
     * @param channe
     * @param name
     * @param description
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvmenuiItem = new MenuItem(channe, name, description);
        //判断数组是否越界
        if(numberOfItems > MAX_ITEMS){
            System.out.println("不好意思,菜单满了....");
        }
        else{
            menuItems[numberOfItems] = tvmenuiItem;
            numberOfItems ++;
        }
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }

}

 

      这里就只介绍两个了。他们完成了各自菜单功能的实现,当你在做三个菜单的统一显示时,你必须要要调用他们的getMenuItems(),来取得他们各个菜单里面的值,但是这个时候问题出现了,你发现他们的返回值都不相同,要想展示出来你必须这么干。

List<MenuItem> tvMenuItems = tvMenu.getMenuItems();
        for(int i = 0 ; i < tvMenuItems.size() ; i++){
            MenuItem menuItem = tvMenuItems.get(i);
            ............
        }

        FilmMenu filmMenu = new FilmMenu();
        MenuItem[] filmItems = filmMenu.getMenuItems();
        for(int i = 0 ; i < filmItems.length ; i++){
            MenuItem menuItem = filmItems[i];
            ............
        }

      在这里我们总是需要来处理这个两个菜单,通过循环来遍历这些菜单选项,如果小安子的实现也不同呢?是不是得要三个循环呢?如果下次有需要增加一个模块呢?在这里你就恼火了,他们为什么不采用相同的实现方式,但是这个时候你也不可能让他们去更改了,他们有太多的代码在依赖着这个两个菜单了,如果更改就意味着要重写很多的代码了。所以你就想是不是可以来封装循环呢?不错就是封装遍历。这就是迭代器模式的动机--能够游走于聚合内的每一个元素,同时还可以提供多种不同的遍历方式。

一、模式定义

      何谓迭代器模式?所谓迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。在实际的开发过程中,我们可能需要针对不同的需求,可能需要以不同的方式来遍历整个整合对象,但是我们不希望在聚合对象的抽象接口层中充斥着各种不同的便利操作。这个时候我们就需要这样一种东西,它应该具备如下三个功能:

      1、能够便利一个聚合对象。

      2、我们不需要了解聚合对象的内部结构。

      3、能够提供多种不同的遍历方式。

      这三个功能就是迭代器模式需要解决的问题。作为一个功能强大的模式,迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这样做就简化了聚合的接口和实现,也可以让聚合更专注在它所应该专注的事情上,这样做就更加符合单一责任原则。

二、模式结构

      下图是迭代器模式UML图。

      从上面可以看书迭代器模式有如下几个角色:

      Iterator: 抽象迭代器:所有迭代器都需要实现的接口,提供了游走聚合对象元素之间的方法。

      ConcreteIterator: 具体迭代器。利用这个具体的迭代器能够对具体的聚合对象进行遍历。每一个聚合对象都应该对应一个具体的迭代器。

      Aggregate: 抽象聚合类。

      ConcreteAggregate: 具体聚合类。实现creatorIterator()方法,返回该聚合对象的迭代器。

三、模式实现

     下面利用迭代器模式对上面案例进行优化整改。下面是正对该实例的UML图。

      首先我们需要定义迭代器接口。Iterator.java

public interface Iterator {
    boolean hasNext();
    Object next();
}

      电影节目的迭代器:FilmMenuIterator.java

public class FilmMenuIterator implements Iterator{
    MenuItem[] menuItems;
    int position = 0;
    
    public FilmMenuIterator(MenuItem[] menuItems){
        this.menuItems = menuItems;
    }
    
    public boolean hasNext() {
        if(position > menuItems.length-1 || menuItems[position] == null){
            return false;
        }
        return true;
    }

    public Object next() {
        MenuItem menuItem = menuItems[position];
        position ++;
        return menuItem;
    }

}

      电视界面的迭代器:TVChanneMenuIterator.java

public class TVChanneMenuIterator implements Iterator{

    List<MenuItem> menuItems;
    int position = 0;
    
    public TVChanneMenuIterator(List<MenuItem> menuItems){
        this.menuItems = menuItems;
    }
    
    public boolean hasNext() {
        if(position > menuItems.size()-1 || menuItems.get(position) == null){
            return false;
        }
        else{
            return true;
        }
    }

    public Object next() {
        MenuItem menuItem = menuItems.get(position);
        position ++;
        return menuItem;
    }
}

      然后是菜单接口,该接口提供返回具体迭代器的方法:createIterator()。

public interface TelevisionMenu {
    public void addItem(int channe,String name,String description);
    public Iterator createIrerator();
}

      两个具体聚合类。这个两个聚合类实现createIterator()方法,分别返回电视界面的聚合类和电影界面的聚合类。


public class FilmMenu implements TelevisionMenu{
    static final int MAX_ITEMS = 5;    //菜单最大长度
    MenuItem[] menuItems;
    int numberOfItems = 0;
    
    /**
     * 构造函数完成初始化
     */
    public FilmMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem(1, "绝世天劫", "这是布鲁斯威利斯主演的...");
        addItem(2, "达芬奇密码", "这是我最喜欢的电影之一...");
        addItem(3, "黑客帝国123", "不知道你能够看懂不???");
        addItem(4, "我的女友是机器人", "一部我不反感的经典爱情电影...");
        addItem(5, "肖申克的救赎", "自由,幸福,离你有多远");
    }
    
    /**
     * @desc 将电影解决添加到菜单项中
     * @param channe
     * @param name
     * @param description
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvmenuiItem = new MenuItem(channe, name, description);
        //判断数组是否越界
        if(numberOfItems > MAX_ITEMS){
            System.out.println("不好意思,菜单满了....");
        }
        else{
            menuItems[numberOfItems] = tvmenuiItem;
            numberOfItems ++;
        }
    }

    public Iterator createIrerator() {
        return new FilmMenuIterator(menuItems);
    }

}

 

public class TVChanneMenu implements TelevisionMenu{
    List<MenuItem> menuItems;
    
    /**
     * 构造函数完成初始化
     */
    public TVChanneMenu(){
        menuItems = new ArrayList<MenuItem>();
        addItem(1, "CCTV-1", "This is CCTV-1");
        addItem(2, "CCTV-2", "This is CCTV-2");
        addItem(3, "CCTV-3", "This is CCTV-3");
        addItem(4, "CCTV-4", "This is CCTV-4");
        addItem(5, "CCTV-5", "This is CCTV-5");
    }
    
    /**
     * @desc 将电视频道节目添加菜单集合中
     * @param channe  频道
     * @param name  名称
     * @param description  描述
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvMenuItem = new MenuItem(channe, name, description);
        menuItems.add(tvMenuItem);
    }

    public Iterator createIrerator() {
        return new TVChanneMenuIterator(menuItems);
    }
    
}

      终于完成了,现在就可以来实现主菜单了,用来展示、遍历所有的电视、电影界面咯。

public class MainMenu {
    TelevisionMenu tvMenu;
    FilmMenu filmMenu;
    
    public MainMenu(TelevisionMenu tvMenu,FilmMenu filmMenu){
        this.tvMenu = tvMenu;
        this.filmMenu  = filmMenu;
    }
    
    public void printMenu(){
        Iterator tvIterator = tvMenu.createIrerator();
        Iterator filmIterator = filmMenu.createIrerator();
        
        System.out.println("电视节目有:");
        printMenu(tvIterator);
        System.out.println("----------------------------------------------------------------");
        System.out.println("电影节目有:");
        printMenu(filmIterator);
    }

    private void printMenu(Iterator iterator) {
        while(iterator.hasNext()){
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.print("channe:"+menuItem.getChanne()+",  ");
            System.out.print("name:"+menuItem.getName()+",  ");
            System.out.println("description :"+menuItem.getDescription());
        }
    }
}

      测试程序:

public class Test {
    public static void main(String[] args) {
        TVChanneMenu tvMenu = new TVChanneMenu();
        FilmMenu filmMenu = new FilmMenu();
        
        MainMenu mainMenu = new MainMenu(tvMenu, filmMenu);
        mainMenu.printMenu();
    }
}

      运行结果

四、模式优缺点

优点

      1、它支持以不同的方式遍历一个聚合对象。

      2、迭代器简化了聚合类。

      3、在同一个聚合上可以有多个遍历。

      4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

缺点

      由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

五、模式使用场景

      1、访问一个聚合对象的内容而无须暴露它的内部表示。

      2、需要为聚合对象提供多种遍历方式。

      3、为遍历不同的聚合结构提供一个统一的接口。

六、模式总结

      1、迭代器模式提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示。

      2、将遍历聚合对象中数据的行为提取出来,封装到一个迭代器中,通过专门的迭代器来遍历聚合对象的内部数据,这就是迭代器模式的本质。迭代器模式是“单一职责原则”的完美体现。

      3、当使用迭代器的时候,我们依赖聚合提供遍历。

      4、迭代器提供了一个通用的接口,让我们遍历聚合的项,放我们编码使用聚合项时,就可以使用多态机制。

<think>嗯,用户问如何回退Git远程提交记录。我需要仔细思考这个问题,确保给出正确的步骤和注意事项。首先,用户可能已经将一些提交推送到远程仓库,但后来发现有问题,想要撤销这些提交。这时候,回退远程提交就需要用到Git的一些命令,比如git reset或者git revert,但要注意不同的方法有不同的影响。 首先,我应该考虑本地回退和强制推送到远程的情况。通常的做法是在本地回退到某个提交,然后使用git push --force来覆盖远程分支。但这样做有风险,尤其是当其他人也在同一个分支上工作时,可能会导致他们的提交丢失。需要提醒用户注意这一点。 接着,可能需要具体步骤。例如,用户需要先使用git log找到要回退到的提交的哈希值,然后用git reset --hard <commit-hash>来回退本地分支。之后,强制推送到远程仓库。但必须强调,只有在确定没有其他人依赖该分支的情况下才能这么做。 另外,git revert也是一个安全的选择,因为它会创建新的提交撤销之前的更改,而不会重写历史。这对于团队合作的项目更安全,因为不会影响其他人的工作流程。但用户的问题明确是回退远程提交,可能他们希望彻底删除某些提交,而不仅仅是撤销更改。 还有,可能需要提到其他相关命令,比如git push --force-with-lease,它比--force更安全,因为它会检查远程分支的当前状态是否与本地预期一致,避免覆盖其他人的提交。 还需要考虑分支保护的情况,比如在GitHub或GitLab上,默认分支可能被保护,不允许强制推送。这时候用户可能需要暂时解除保护,或者在临时分支上操作,然后通过合并请求来回退。 此外,提醒用户在操作前备份当前状态,比如创建一个临时分支,以防万一操作出错可以恢复。同时,强调在执行强制推送后,其他协作者需要同步他们的本地仓库,避免出现不一致的情况。 总结一下,回答的结构应该是先说明风险,再给出不同的方法(reset + force push,revert),比较它们的适用场景,然后分步骤说明操作流程,最后给出注意事项和建议。需要确保用户理解每种方法的后果,并根据实际情况选择合适的方式。</think>要回退Git远程提交记录,需谨慎操作以避免影响团队协作。以下是具体操作步骤及注意事项: --- ### 一、操作原理 Git远程提交记录本质是修改分支指针位置,通过本地回退后强制推送(force push)覆盖远程分支。⚠️ 此操作会重写历史记录,可能影响其他协作者。 --- ### 二、具体步骤 #### 方法1:使用 `git reset`(彻底删除提交) 1. **定位目标提交** ```bash git log --oneline # 找到要回退到的提交哈希值(如 abc123) ``` 2. **本地回退** ```bash git reset --hard abc123 # 将本地分支回退到目标提交 ``` 3. **强制推送到远程** ```bash git push --force origin 分支名 # 覆盖远程分支 # 更安全的替代命令: git push --force-with-lease origin 分支名 # 检查远程状态是否与本地预期一致 ``` #### 方法2:使用 `git revert`(安全撤销,保留历史) 1. **撤销指定提交** ```bash git revert 要撤销提交哈希值 # 生成一个反向提交 ``` 2. **推送新提交** ```bash git push origin 分支名 # 无需强制推送 ``` --- ### 三、关键对比 | 方法 | 适用场景 | 优点 | 缺点 | |------------|------------------------------|---------------------|-----------------------| | `git reset`| 个人分支/需要彻底
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值