精华内容
下载资源
问答
  • 迭代模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。 爆炸性新闻:对象村餐厅和对象村煎饼屋合并了! 真是个好消息!现在我们可以在同一个地方,享用煎饼屋美味的煎饼早餐,和...

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

    爆炸性新闻:对象村餐厅和对象村煎饼屋合并了!

    真是个好消息!现在我们可以在同一个地方,享用煎饼屋美味的煎饼早餐,和好吃的餐厅午餐了。但是,好像有一点小麻烦:

    新的餐厅想用煎饼屋菜单当作早餐的菜单,使用餐厅的菜单当做午餐的菜单,大家都同意了这样实现菜单项。但是大家无法同意菜单的实现。煎饼屋使用ArrayList记录他的菜单项,而餐厅使用的是数组。他们两个都不愿意改变他们的实现,毕竟有太多代码依赖于它们了。

    检查菜单项

    让我们先检查每份菜单上的项目和实现。

    public class MenuItem {
        // 名称
        String name;
        // 描述
        String description;
        // 是否为素食
        boolean vegetarian;
        // 价格
        double price;
        public MenuItem(String name,
                        String description,
                        boolean vegetarian,
                        double price) {
            this.name = name;
            this.description = description;
            this.vegetarian = vegetarian;
            this.price = price;
        }
        public String getName() {
            return name;
        }
        public String getDescription() {
            return description;
        }
        public double getPrice() {
            return price;
        }
        public boolean isVegetarian() {
            return vegetarian;
        }
    }

    两个餐厅的菜单实现

    我们先来看看两个餐厅的菜单实现

    // 这是煎饼屋的菜单实现
    public class PancakeHouseMenu {
        // 煎饼屋使用一个ArrayList存储他的菜单项
        ArrayList menuItems;
        public PancakeHouseMenu() {
            menuItems = new ArrayList();
            // 在菜单的构造器中,每一个菜单项都会被加入到ArrayList中
            // 每个菜单项都有一个名称、一个描述、是否为素食、还有价格
            addItem("K&B's Pancake Breakfast",
                    "Pancakes with scrambled eggs, and toast",
                    true,
                    2.99);
            addItem("Regular Pancake Breakfast",
                    "Pancakes with fried eggs, sausage",
                    false,
                    2.99);
            addItem("Blueberry Pancakes",
                    "Pancakes made with fresh blueberries",
                    true,
                    3.49);
            addItem("Waffles",
                    "Waffles, with your choice of blueberries or strawberries",
                    true,
                    3.59);
        }
        // 要加入一个菜单项,煎饼屋的做法是创建一个新的菜单项对象,
        // 传入每一个变量,然后将它加入ArrayList中
        public void addItem(String name, String description, 
                            boolean vegetarian, double price) {
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            menuItems.add(menuItem);
        }
        // 这个方法返回菜单项列表
        public ArrayList getMenuItems() {
            return menuItems;
        }
        // 这里还有菜单的其他方法,这些方法都依赖于这个ArrayList,所以煎饼屋不希望重写全部的代码!
        // ...
    }
    // 餐厅的菜单实现
    public class DinnerMenu {
        // 餐厅采用使用的是数组,所以可以控制菜单的长度,
        // 并且在取出菜单项时,不需要转型
        static final int MAX_ITEMS = 6;
        int numberOfItems = 0;
        MenuItem[] menuItems;
        public DinnerMenu() {
            menuItems = new MenuItem[MAX_ITEMS];
            // 和煎饼屋一样,餐厅使用addItem()辅助方法在构造器中创建菜单项的
            addItem("Vegetarian BLT",
                    "(Fakin') Bacon with lettuce & tomato on whole wheat",
                    true,
                    2.99);
            addItem("BLT",
                    "Bacon with lettuce & tomato on whole wheat",
                    false,
                    2.99);
            addItem("Soup of the day",
                    "Soup of the day, with a side of potato salad",
                    false,
                    3.29);
            addItem("Hotdog",
                    "A hot dog, with saurkraut, relish, onions, topped with cheese",
                    false,
                    3.05);
        }
        public void addItem(String name, String description,
                            boolean vegetarian, double price) {
            // 餐厅坚持让菜单保持在一定的长度之内
            MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
            if (numberOfItems >= MAX_ITEMS) {
                System.err.println("Sorry, menu is full! Can't add item to menu");
            } else {
                menuItems[numberOfItems] = menuItem;
                numberOfItems = numberOfItems + 1;
            }
        }
        // getMenuItems()返回一个菜单项的数组
        public MenuItem[] getMenuItems() {
            return menuItems;
        }
        // 正如煎饼屋那样,这里还有很多其他的菜单代码依赖于这个数组
        // ...
    }

    两种不同的菜单表现方式,会带来什么问题?

    想了解为什么有两种不同的菜单表现方式会让事情变得复杂化,让我们试着实现一个同时使用这两个菜单的客户代码。假设你已经被两个餐厅合租的新公司雇用,你的工作是创建一个Java版本的女招待,她能应对顾客的需要打印定制的菜单,甚至告诉你是否某个菜单项是素食的,而无需询问厨师。跟我们来看看这份关于女招待的规格,然后看看如何实现她。

    Java版本的女招待规格:


    我们先从实现printMenu()方法开始:

    1.打印每份菜单上的所有项,必须调用PancakeHouseMenu和DinnerMenu的getMenuItems()方法,来取得它们各自的菜单项。请注意,两者的返回类型是不一样的。

    // getMenuItems()方法看起来是一样的,但是调用所返回的结果却是不一样的类型。
    // 早餐项是在一个ArrayList中,午餐项则是在一个数组中
    PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
    ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();
    DinnerMenu dinnerMenu = new DinnerMenu();
    MenuItem[] lunchItems = dinnerMenu.getMenuItems();

    2.现在,想要打印PancakeHouseMenu的项,我们用循环将早餐ArrayList内的项一一列出来。想要打印DinnerMenu的项目,我们用循环将数组内的项一一列出来。

    // 现在,我们必须实现两个不同的循环,个别处理这两个不同的菜单
    for (int i = 0; i < breakfastItems.size(); i++) {
        MenuItem menuItem = (MenuItem) breakfastItems.get(i);
        System.out.print(menuItem.getName() + " ");
        System.out.print(menuItem.getPrice() + " ");
        System.out.println(menuItem.getDescription() + " ");
    }
    for (int i = 0; i < lunchItems.length; i++) {
        MenuItem menuItem = lunchItems[i];
        System.out.print(menuItem.getName() + " ");
        System.out.print(menuItem.getPrice() + " ");
        System.out.println(menuItem.getDescription() + " ");
    }

    3.实现女招待中的其他方法,做法也都和这里的方法相类似。我们总是需要处理两个菜单,并且用两个循环遍历这些项。如果还有第三家餐厅以不同的实现出现,我们就需要有三个循环。

    下一步呢?

    这两个餐厅让我们很为难,他们都不想改变自身的实现,因为意味着要重写许多代码。但是如果他们其中一人不肯退让,我们就很难办了,我们所写出来的女招待程序将难以维护、难以扩展。

    如果我们能够找到一个方法,让他们的菜单实现一个相同的接口,该有多好!这样一来,我们就可以最小化女招待代码中的具体引用,同时还有希望摆脱遍历这两个菜单所需的多个循环。

    听起来很棒!但要怎么做呢?

    如果你从本书中学到了一件事情,那就是封装变化的部分。很明显,在这里发生变化的是:由不同的集合(collection)类型所造成的遍历。但是,这能够被封装吗?让我们来看看这个想法:

    1.要遍历早餐项,我们需要使用ArrayList的size()和get()方法:

    for (int i = 0; i < breakfastItems.size(); i++) {
        MenuItem menuItem = (MenuItem) breakfastItems.get(i);
    }

    2.要遍历午餐项,我们需要使用数组的length字段和中括号:

    for (int i = 0; i < lunchItems.length; i++) {
        MenuItem menuItem = lunchItems[i];
    }

    3.现在我们创建一个对象,把它称为迭代器(Iterator),利用它来封装“遍历集合内的每个对象的过程”。先让我们在ArrayList上试试:

    // 我们从breakfastMenu中取得一个菜单项迭代器
    Iterator iterator = breakfastMenu.createIterator();
    // 当还有其他项时
    while (iterator.hasNext()) {
        // 取得下一项
        MenuItem menuItem = (MenuItem) iterator.next();
    }

    4.将它也在数组上试试:

    // 这里的情况也是一样的:客户只需要调用hasNext()和next()即可,
    // 而迭代器会暗中使用数组的下标
    Iterator iterator = lunchMenu.createIterator();
    while (iterator.hasNext()) {
        MenuItem menuItem = (MenuItem) iterator.next();
    }

    会见迭代器模式

    看起来我们对遍历的封装已经奏效了;你大概也已经猜到,这正是一个设计模式,称为迭代器模式。

    关于迭代器模式,你所需要知道的第一件事情,就是它依赖于一个名为迭代器的接口。

    public interface Iterator {
        // hasNext()方法返回一个布尔值,让我们知道是否还有更多的元素
        boolean hasNext();
        // next()方法返回下一个元素
        Object next();
    }

    现在,一旦我们有了这个接口,就可以为各种对象集合实现迭代器:数组、列表、散列表……

    让我们继续实现这个迭代器,并将它挂钩到DinnerMenu中,看它是如何工作的。

    用迭代器改写餐厅菜单

    现在我们需要实现一个具体的迭代器,为餐厅菜单服务:

    public class DinnerMenuIterator implements Iterator {
        MenuItem[] items;
        // position记录当前数组遍历的位置
        int position = 0;
        // 构造器需要被传入一个菜单项的数组当做参数
        public DinnerMenuIterator(MenuItem[] items) {
            this.items = items;
        }
        // next()方法返回数组内的下一项,并递增其位置
        public Object next() {
            MenuItem menuItem = items[position];
            position = position + 1;
            return menuItem;
        }
        // hasNext()方法会检查我们是否已经取得数组内所有的元素。
        // 如果还有元素待遍历,则返回true
        public boolean hasNext() {
            if (position >= items.length || items[position] == null) {
                return false;
            } else {
                return true;
            }
        }
    }

    好了,我们已经有了迭代器。现在就利用它来改写餐厅菜单:我们只需要加入一个方法创建一个DinnerMenuIterator,并将它返回给客户:

    public class DinnerMenu {
        static final int MAX_ITEMS = 6;
        int numberOfItems = 0;
        MenuItem[] menuItems;
        // ...
        // 我们不再需要getMenuItems()方法,事实上,我们根本不想要这个方法,
        // 因为它会暴露我们内部的实现。
        // 这是createIterator()方法,用来从菜单项数组创建一个DinnerMenuIterator,
        // 并将它返回给客户
        public Iterator createIterator() {
            return new DinnerMenuIterator(menuItems);
        }
        // ...
    }

    现在将迭代器代码整合进女招待中。

    public class Waitress {
        PancakeHouseMenu pancakeHouseMenu;
        DinnerMenu dinnerMenu;
        // 在构造器中,女招待照顾两个菜单
        public Waitress(PancakeHouseMenu pancakeHouseMenu, DinnerMenu dinnerMenu) {
            this.pancakeHouseMenu = pancakeHouseMenu;
            this.dinnerMenu = dinnerMenu;
        }
        public void printMenu() {
            // 这个printMenu()方法为每一个菜单各自创建一个迭代器
            Iterator pancakeIterator = pancakeHouseMenu.createIterator();
            Iterator dinnerIterator = dinnerMenu.createIterator();
            // 然后调用重载的printMenu(),将迭代器传入
            printMenu(pancakeIterator);
            printMenu(dinnerIterator);
        }
        // 这个重载的printMenu()方法,使用迭代器来遍历菜单项并打印出来
        private void printMenu(Iterator iterator) {
            while (iterator.hasNext()) {
                MenuItem menuItem = (MenuItem) iterator.next();
                System.out.println(menuItem.getName() + " " + 
                        menuItem.getPrice() + " " + menuItem.getDescription());
            }
        }
    }

    到目前为止,我们做了些什么?

    首先,我们让对象村的厨师们非常快乐。他们可以保持他们自己的实现又可以摆平差别。只要我们给他们这两个迭代器(PancakeHouseMenuIterator和DinnerMenuIterator),他们只需要加入一个createIterator()方法,一切就大功告成了。

    这个过程中,我们也帮了我们自己。女招待将会更容易维护和扩展。让我们来彻底检查一下到底我们做了哪些事,以及后果如何:


    做一些改良

    好了,我们已经知道这两份菜单的接口完全一样,但没有为它们设计一个共同的接口。所以,接下来就要这么做,让女招待更干净一些。

    Java有一个内置的Iterator接口,让我们先来看看:

    public interface Iterator<E> {
        /**
         * Returns true if there is at least one more element, false otherwise.
         * @see #next
         */
        public boolean hasNext();
        /**
         * Returns the next object and advances the iterator.
         *
         * @return the next object.
         * @throws NoSuchElementException
         *             if there are no more elements.
         * @see #hasNext
         */
        public E next();
        /**
         * Removes the last object returned by {@code next} from the collection.
         * This method can only be called once between each call to {@code next}.
         *
         * @throws UnsupportedOperationException
         *             if removing is not supported by the collection being
         *             iterated.
         * @throws IllegalStateException
         *             if {@code next} has not been called, or {@code remove} has
         *             already been called after the last call to {@code next}.
         */
        public void remove();
    }

    这个接口看起来和我们之前定义的一样,只不过多了一个附加的方法,允许我们从聚合中删除由next()方法返回的最后一项。

    接下来让我们用java.util.Iterator来清理代码。

    让我们先从煎饼屋菜单开始,先把它改用java.util.Iterator,这很容易,只需要删除煎饼屋菜单迭代器,然后在煎饼屋菜单的代码前面加上 import java.util.Iterator。再改变下面这一行代码就可以了:

    public Iterator createIterator() {
        return menuItems.iterator();
    }

    这样PancakeHouseMenu就完成了。

    接着,我们处理DinnerMenu,以符合java.util.Iterator的需求。

    public class DinnerMenuIterator implements Iterator {
        MenuItem[] items;
        int position = 0;
        public DinnerMenuIterator(MenuItem[] items) {
            this.items = items;
        }
        public Object next() {
            MenuItem menuItem = items[position];
            position = position + 1;
            return menuItem;
        }
        public boolean hasNext() {
            if (position >= items.length || items[position] == null) {
                return false;
            } else {
                return true;
            }
        }
        // 我们需要实现remove()方法。因为使用的是固定长度的数组,
        // 所以在remove()方法被调用时,我们将后面的所有元素往前移动一个位置。
        @Override
        public void remove() {
            if (position <= 0) {
                throw new IllegalStateException("You can't remove
                 an item until you've done at least one next()");
            }
            if (items[position - 1] != null) {
                for (int i = position-1; i < (items.length - 1); i++) {
                    items[i] = items[i + 1];
                }
                items[items.length - 1] = null;
            }
        }
    }

    我们只需要给菜单一个共同的接口,然后再稍微改一下女招待。这个Menu接口相当简单:

    public interface Menu {
        public Iterator createIterator();
    }

    现在,我们需要让煎饼屋菜单类和餐厅菜单类都实现Menu接口,然后更新女招待的代码:

    public class Waitress {
        Menu pancakeHouseMenu;
        Menu dinnerMenu;
        // 将具体菜单类改成Menu接口
        public Waitress(Menu pancakeHouseMenu, Menu dinnerMenu) {
            this.pancakeHouseMenu = pancakeHouseMenu;
            this.dinnerMenu = dinnerMenu;
        }
        // 以下的代码没有修改
        public void printMenu() {
            Iterator pancakeIterator = pancakeHouseMenu.createIterator();
            Iterator dinnerIterator = dinnerMenu.createIterator();
            printMenu(pancakeIterator);
            printMenu(dinnerIterator);
        }
        private void printMenu(Iterator iterator) {
            while (iterator.hasNext()) {
                MenuItem menuItem = (MenuItem) iterator.next();
                System.out.println(menuItem.getName() + " " +
                        menuItem.getPrice() + " " + menuItem.getDescription());
            }
        }
    }

    这为我们带来了什么好处?煎饼屋菜单和餐厅菜单的类,都实现了Menu接口,女招待可以利用接口(而不是具体类)引用每一个菜单对象。这样,通过“针对接口编程,而不针对实现编程”,我们就可以减少女招待和具体类之间的依赖。

    定义迭代器模式

    现在我们来看看这个模式的正式定义:

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

    迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露内部的表示。把游走的任务放在迭代器上,而不是聚合上,这样简化了聚合的接口和实现,也让责任各得其所。

    这很有意义:这个模式给你提供了一种方法,可以顺序访问一个聚集对象中的元素,而又不用知道内部是如何表示的。你已经在前面的两个菜单实现中看到了这一点。在设计中使用迭代器的影响是明显的:如果你有一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配使用,如同前面的printMenu()方法一样,只要有了迭代器这个方法,根本不管菜单项究竟是由数组还是由ArrayList(或者其他能创建迭代器的东西)来保存的。

    另一个对你的设计造成重要影响的,是迭代器模式把这些元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面(也就是管理对象组合),而不必去理会遍历的事情。

    单一责任

    如果我们允许我们的聚合实现它们内部的集合,以及相关的操作和遍历的方法,又会如何?我们已经知道这会增加聚合中的方法个数,但又怎样呢?为什么这么做不好?

    想知道为什么,首先你需要认清楚,当我们允许一个类不但要完成自己的事情(管理某种聚合),还同时要担负更多的责任(例如遍历)时,我们就给了这个类两个变化的原因。两个?没错,就是两个!如果这个集合改变的话,这个类也必须改变,如果我们遍历的方式改变的话,这个类也必须跟着改变。所以,再一次地,我们的老朋友“改变”又成了我们设计原则的中心:

    设计原则:一个类应该只有一个引起变化的原因

    我们知道要避免类内的改变,因为修改代码很容易造成许多潜在的错误。如果有一个类具有两个改变的原因,那么这会使得将来该类的变化几率上升,而当它真的改变时,你的设计中同时有两个方面将会受到影响。

    没错,这听起来很容易,但其实做起来并不简单:区分设计中的责任,是最困难的事情之一。我们的大脑很习惯看着一大群的行为,然后将它们集中在一起,尽管他们可能属于两个或者多个不同的责任。想要成功的唯一方法,就是努力不懈地检查你的设计,随着系统的成长,随时观察有没有迹象显示某个类改变的原因超出一个。

    展开全文
  • 迭代模式

    2016-07-30 15:27:23
    爆炸性新闻:对象村餐厅和对象村煎饼屋合并 检查菜单项 public class MenuItem { String name; String description; boolean vegetarian; double price; public MenuItem(String name, String ...

    爆炸性新闻:对象村餐厅和对象村煎饼屋合并


    检查菜单项

    public class MenuItem {
    	String name;
    	String description;
    	boolean vegetarian;
    	double price;
    	public MenuItem(String name, String description, boolean vegetarian,double price
    			) {
    		
    		this.description = description;
    		this.name = name;
    		this.price = price;
    		this.vegetarian = vegetarian;
    	}
    	public String getName() {
    		return name;
    	}
    	public String getDescription() {
    		return description;
    	}
    	public boolean isVegetarian() {
    		return vegetarian;
    	}
    	public double getPrice() {
    		return price;
    	}
    	
    }


    lou和mel的菜单实现

    public class PancakeHouseMenu {
    	
    	ArrayList menuItems;
    
    	public PancakeHouseMenu() {
    		menuItems = new ArrayList();
    		addItem("K&B's Pancake Breakfast","Pancakes with scrambled eggs, and toast",true,2.99);
    		addItem("Regular Pancake Breakfast","Pancakes with fried eggs, sausage",false,2.99);
    		addItem("Blueberry Pancakes","Pancakes made with fresh blueberries",true,2.99);
    		addItem("Waffles","Waffles, with your choice of blueberries or strawberries",true,2.99);
    	}
    	public void addItem(String name,String description,boolean vegetarian,double price){
    		MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
    		menuItems.add(menuItem);
    	}
    	public ArrayList getMenuItems(){
    		return menuItems;
    	}
    	
    }

    public class DinerMenu {
    	
    	static final int MAX_ITEMS = 6;
    	int numberOfItems = 0;
    	MenuItem[] menuItems;
    	public DinerMenu(){
    		menuItems = new MenuItem[MAX_ITEMS];
    		addItem("Vegetarian BLT","(Fakin') Bacon with lettuce & tomato on whole wheat",true,2.99);
    		addItem("BLT","Bacon with lettuce & tomato on whole wheat",false,2.99);
    		addItem("Soup of the day","Soup of the day ,with a side of potato salad",false,3.29);
    		addItem("Hotdog","A hot dog , with saurkraut , relish , onions , topped with cheese",false,3.05);
    	}
    	public void addItem(String name,String description,boolean vegetarian,double price){
    		MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
    		if(numberOfItems>=MAX_ITEMS){
    			System.err.println("sorry ,menu is full! can't add item to menu");
    		}else {
    			menuItems[numberOfItems] = menuItem;
    			numberOfItems = numberOfItems + 1;
    		}
    	}
    	public MenuItem[] getMenuItems(){
    		return menuItems;
    	}
    	
    }


    定义迭代器接口

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


    餐厅实现迭代器接口

    public class DinerMenuIterator implements Iterator {
    	MenuItem[] items;
    	int postion = 0;
    	
    	
    	public DinerMenuIterator(MenuItem[] items) {
    		this.items = items;
    	}
    
    	public boolean hasNext() {
    		if(postion>=items.length || items[postion] == null){
    			return false;
    		}else{
    			return true;
    		}
    	}
    
    	public Object next() {
    		MenuItem menuItem = items[postion];
    		postion = postion +1;
    		return menuItem;
    	}
    
    }

    煎饼店实现迭代器接口

    public class PancakeHouseIterator implements Iterator{
    	ArrayList<MenuItem> items;
        int position = 0;
    	public PancakeHouseIterator(ArrayList<MenuItem> items) {
    		
    		this.items = items;
    	}
    
    	public boolean hasNext() {
    		if(position>=items.size()||items.get(position)==null){
    			return false;
    		}else{
    			return true;
    		}
    	}
    
    	public Object next() {
    		MenuItem menuItem = items.get(position);
    		position++;
    		return menuItem;
    	}
    
    }

    用迭代器改写餐厅菜单

    public class DinerMenu {
    
    	static final int MAX_ITEMS = 6;
    	int numberOfItems = 0;
    	MenuItem[] menuItems;
    	public DinerMenu(){
    		menuItems = new MenuItem[MAX_ITEMS];
    		addItem("Vegetarian BLT","(Fakin') Bacon with lettuce & tomato on whole wheat",true,2.99);
    		addItem("BLT","Bacon with lettuce & tomato on whole wheat",false,2.99);
    		addItem("Soup of the day","Soup of the day ,with a side of potato salad",false,3.29);
    		addItem("Hotdog","A hot dog , with saurkraut , relish , onions , topped with cheese",false,3.05);
    	}
    	
    	public void addItem(String name,String description,boolean vegetarian,double price){
    		MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
    		if(numberOfItems>=MAX_ITEMS){
    			System.err.println("sorry ,menu is full! can't add item to menu");
    		}else {
    			menuItems[numberOfItems] = menuItem;
    			numberOfItems = numberOfItems + 1;
    		}
    	}
    
    	public Iterator createIterator(){
    		return new DinerMenuIterator(menuItems);
    	}
    	
    	
    	
    }

    用迭代器改写煎饼菜单

    public class PancakeHouseMenu {
    
    	ArrayList menuItems;
    
    	public PancakeHouseMenu() {
    		menuItems = new ArrayList();
    		addItem("K&B's Pancake Breakfast","Pancakes with scrambled eggs, and toast",true,2.99);
    		addItem("Regular Pancake Breakfast","Pancakes with fried eggs, sausage",false,2.99);
    		addItem("Blueberry Pancakes","Pancakes made with fresh blueberries",true,2.99);
    		addItem("Waffles","Waffles, with your choice of blueberries or strawberries",true,2.99);
    	}
    	public PancakeHouseMenu(ArrayList<MenuItem> pancakeItems) {
    	}
    	public void addItem(String name,String description,boolean vegetarian,double price){
    		MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
    		menuItems.add(menuItem);
    	}
    
    	public Iterator createIterator(){
    		return new PancakeHouseIterator(menuItems);
    	}
    }

    女招待的实现代码

    public class Waitress {
    	PancakeHouseMenu pancakeHouseMenu;
    	DinerMenu dinerMenu;
    	public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
    		this.dinerMenu = dinerMenu;
    		this.pancakeHouseMenu = pancakeHouseMenu;
    	}
    	public void printMenu(){
    		Iterator pancakeIterator= pancakeHouseMenu.createIterator();
    		Iterator dinerIterator = dinerMenu.createIterator();
    		System.out.println("MENU\n-----------\nBREAKFAST");
    		
    		printMenu(pancakeIterator);
    		System.out.println("\nLUNCH");
    		
    		printMenu(dinerIterator);
    	}
    	private void printMenu(Iterator iterator) {
    		while(iterator.hasNext()){
    			MenuItem menuItem = (MenuItem) iterator.next();
    			System.out.println(menuItem.getName()+", ");
    			System.out.println(menuItem.getPrice()+", ");
    			System.out.println(menuItem.getDescription()+",");
    		}
    		
    	}
    	
    	
    }

    测试迭代器的代码

    public class MenuTestDrive {
    	public static void main(String[] args) {
    		PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
    		DinerMenu dinerMenu = new DinerMenu();
    		Waitress waitress = new Waitress(pancakeHouseMenu,dinerMenu);
    		waitress.printMenu();
    	}
    }

    输出结果:

    MENU
    -----------
    BREAKFAST
    K&B's Pancake Breakfast, 
    2.99, 
    Pancakes with scrambled eggs, and toast,
    Regular Pancake Breakfast, 
    2.99, 
    Pancakes with fried eggs, sausage,
    Blueberry Pancakes, 
    2.99, 
    Pancakes made with fresh blueberries,
    Waffles, 
    2.99, 
    Waffles, with your choice of blueberries or strawberries,
    
    LUNCH
    Vegetarian BLT, 
    2.99, 
    (Fakin') Bacon with lettuce & tomato on whole wheat,
    BLT, 
    2.99, 
    Bacon with lettuce & tomato on whole wheat,
    Soup of the day, 
    3.29, 
    Soup of the day ,with a side of potato salad,
    Hotdog, 
    3.05, 
    A hot dog , with saurkraut , relish , onions , topped with cheese,




    展开全文
  • 迭代模式-引导篇 这两天,比较火的并购新闻就是,网易考拉被阿里以20亿美元收购。从此网易考拉不再姓“网”而姓“阿”了。并购后的网易考拉和阿里的电商系统进行对接。那么问题来了:在阿里有个早餐店的菜单...

    迭代器模式-引导篇

    这两天,比较火的并购新闻就是,网易考拉被阿里以20亿美元收购。从此网易考拉不再姓“网”而姓“阿”了。并购后的网易考拉和阿里的电商系统进行对接。那么问题来了:在阿里有个早餐店的菜单(CakeHouseMenu)使用的事ArrayList来存放菜单的,考拉有个午餐店的菜单(DinerMenu)使用的是数组结构存放的。现在考拉和阿里合并了,两个点的菜单也要合并。

    我们先来看看第一版设计:

    因为马爸爸说了,国庆之前,必须合并上线,时间紧任务中,肿么办?那就再创建一个对象,使用一个菜单对象,将早餐店对象机午餐店对象作为属性,调用的时候,直接调用各自对象的就可以。类图如下:

    顾客来了,点早餐,服务器就从菜单中调用早餐店的get方法。得到KFC早餐套餐

    如果点的是午餐,就从菜单中调用午餐店的getMenuItem方法,得到快餐一份。

    代码如下:

    运行ConventionalMainTest运行结果:

    我们可以看到,早餐、午餐菜单也都打印出来了。正常啊,没问题啊。

    我们先来看看服务员(waitress)对象里面内容:

    从上图中,我们可以看到在服务员对象中有早餐店对象、午餐店对象、list类型的items以及数组类型的items。从运行结果上来看,是没有问题的。但是要是过了N+X天后,马爸爸又玩起了收购肿么办?假设收购的是X店。X店的菜单使用的是hashTable这种类型的。

    难道,我们要在waitress中在添加X店对象同时添加hashTabel类型的items吗?好,就算收购一个,添加一个可以。

    那么如果收购了M+N个店。菜单数据类型使用了W种类型。难道,每次都修改waiters这个类吗?

     

    这样行是行,但是在后期维护、管理比较麻烦。而且还违背了开闭原则(对修改是封闭的,对扩展是开放的)。那么怎么办呢?

    来源:凯哥Java(kaigejava)。

    凯哥个人博客:www.kaigejava.com

    思考:

    我们在开发的时候,针对接口开发,这样耦合度也可以降低。我们假设两个饭店的菜单都实现了一个接口。然后waiter对象只要拥有接口对象就可以。

    封装遍历的顶级接口,迭代器类图如下:

    我们用迭代器接口来修改菜单:

    说明:

    CakeHouseIterator和DinerIterator两个类是实现了Iterator接口的

    修改两个饭店获取getIterator的方法。返回对应放到实现iterator接口的对象。

    我们来看早餐店的iterator对象:

    在重写hasNext机next方法。

    我们在来看看修改后的服务员对象:

    这个时候,服务员对象只有iterator对象了。已经实现了对早餐店及午餐店的解耦。

    再来看看测试类:

    在服务员对象添加菜单的时候,是不知道具体添加的是早餐店的菜单还是午餐店的菜单。实现了解耦。

    这样做的好处:

    一:类之间实现了松耦合

    二:就算考拉修改了菜单数据结构也不影响服务员的点餐。也是实现耦合的一种表现。

    不写了,太困了。已经7号凌晨一点多了。各位看官,今日太累了,写不不好,在迭代器总结篇好好补上。

     

    本文作者:凯哥Java(kaigejava)

     

    转载于:https://www.cnblogs.com/kaigejava/p/11479122.html

    展开全文
  • 学习软件设计,向OO高手迈进! 设计模式(Design pattern)是软件开发...什么是迭代模式 在本文末尾会给出解释,待耐心看完demo再看定义,相信你会有更深刻的印象 实例讲解 背景 爆炸性新闻:隔壁的餐厅和煎饼屋被A...

    学习软件设计,向OO高手迈进!
    设计模式(Design pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案。
    这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
    是前辈大神们留下的软件设计的"招式"或是"套路"。

    什么是迭代器模式

    在本文末尾会给出解释,待耐心看完demo再看定义,相信你会有更深刻的印象

    实例讲解

    背景

    爆炸性新闻:隔壁的餐厅煎饼屋被A公司收购了,A公司想用煎饼屋菜单当做早餐的菜单,用餐厅菜单当做午餐的菜单。
    餐厅有餐厅的代码实现自己的菜单,煎饼屋也有自己的代码实现菜单。
    A公司找到了我们,让我们帮它实现一个能应对顾客需要打印定制的菜单的功能。

    目前我们得知,餐厅和煎饼屋都同意实现 MenuItem,MenuItem 是A公司的菜单类
    菜单类包含了名称、叙述、是否为素食,还有价格。来看看代码

    class MenuItem {
    public:
        MenuItem(string name, string description, bool vegetarian, double price) {
            m_strName = name;
            m_strDescription = description;
            m_bVegetarian = vegetarian;
            m_fPrice = price;
        }
        string GetName(void) {
            return m_strName;
        }
        string GetDescription(void) {
            return m_strDescription;
        }
        bool IsVegetarian(void) {
            return m_bVegetarian;
        }
        double GetPrice(void) {
            return m_fPrice;
        }
    private:
        string m_strName;
        string m_strDescription;
        bool m_bVegetarian;
        double m_fPrice;
    };
    

    再来看看餐厅和煎饼屋的菜单实现
    煎饼屋的菜单实现,用的是 vector 容器来存储菜单对象,他们认为这样可以轻易的扩展菜单。

    class PancakeHouseMenu {
    public:
        PancakeHouseMenu() {
            m_aMenuItems.clear();
            // 炒蛋吐司煎饼
            AddItem("A Pancake", "Pancakes with scrambled eggs and toast", true, 2.99);
            // 煎蛋香肠煎饼
            AddItem("B Pancake", "Pancakes with fried eggs and sausage", false, 2.99);
            // 蓝莓煎饼
            AddItem("C Pancake", "Pancakes with fresh blueberries", true, 3.49);
            // 草莓华夫饼
            AddItem("D Pancake", "Waffles with strawberries", true, 3.59);
        }
        void AddItem(string name, string description, bool vegetarian, double price) {
            MenuItem *pMenuItem = new MenuItem(name, description, vegetarian, price);
            m_aMenuItems.push_back(pMenuItem);
        }
        vector<MenuItem *> GetMenuItems(void) {
            return m_aMenuItems;
        }
    private:
        vector<MenuItem *> m_aMenuItems; // 容器的形式
    };
    

    餐厅的菜单实现,用的是数组来存储菜单对象,他们认为这样可以控制菜单的长度,而且在取出菜单的时候,也不需要强转。

    class DiningRoomMenu {
    public:
        DiningRoomMenu() {
            memset(m_aMenuItems, 0, sizeof(m_aMenuItems));
            m_nNumberOfItems = 0;
            // 素食全麦培根
            AddItem("A Dinner", "Vegetarian bacon with lettuce & tomato on whole wheat", true, 2.99);
            // 全麦培根
            AddItem("B Dinner", "Bacon with lettuce & tomato on whole wheat", false, 2.99);
            // 例汤
            AddItem("C Dinner", "Soup of the day, with a side of potato salad", false, 3.29);
            // 热狗
            AddItem("D Dinner", "Hotdog with saurkraut topped with cheese", false, 3.05);
        }
        void AddItem(string name, string description, bool vegetarian, double price) {
            MenuItem *pMenuItem = new MenuItem(name, description, vegetarian, price);
            if(m_nNumberOfItems >= m_nMaxItems) {
                printf("Sorry, menu is full! Can not add item to menu!\n");
            } else {
                m_aMenuItems[m_nNumberOfItems] = pMenuItem;
                m_nNumberOfItems++;
            }
        }
        MenuItem** GetMenuItems(int &maxItems) {
            maxItems = m_nMaxItems;
            return m_aMenuItems;
        }
    private:
        const static int m_nMaxItems = 6;
        int m_nNumberOfItems;
        MenuItem *m_aMenuItems[m_nMaxItems]; // 数组的形式
    };
    

    餐厅和煎饼屋在菜单的存储方式上花费了很多时间和代码,煎饼屋有许多的菜单代码,都依赖于这个 vector 容器,所以他们不希望重写全部的代码;同样,餐厅也有很多菜单代码都依赖于这个数组,他们没空重写这么多代码

    有两种不同的菜单表现方式,这会带来什么问题?
    这会让事情变得复杂(客户代码不好写),事实说话,让我们来实现一个同时使用这两个菜单的客户代码

    Version 1.0

    客户代码,在此是 main 函数
    GetMenuItems() 方法所返回的类型是不一样的,所以我们必须实现两个不同的循环,个别处理不同的菜单

    int main(int argc, char *argv[]) {
        PancakeHouseMenu *pPancakeHouseMenu = new PancakeHouseMenu();
        DiningRoomMenu *pDiningRoomMenu = new DiningRoomMenu();
    
        vector<MenuItem *> aBreakfastItems = pPancakeHouseMenu->GetMenuItems();
        int nMaxItems = 0;
        MenuItem **aLunchItems = pDiningRoomMenu->GetMenuItems(nMaxItems);
        
        // 只打印早餐项
        for(int i = 0; i < aBreakfastItems.size(); i++) {
            MenuItem *pMenuItem = aBreakfastItems.at(i);
            printf("%s - ", pMenuItem->GetName().c_str());
            printf("%.2f - ", pMenuItem->GetPrice());
            printf("%s\n", pMenuItem->GetDescription().c_str());
        }
        // 只打印午餐项
        for(int i = 0; i < nMaxItems && aLunchItems[i]; i++) {
            MenuItem *pMenuItem = aLunchItems[i];
            printf("%s - ", pMenuItem->GetName().c_str());
            printf("%.2f - ", pMenuItem->GetPrice());
            printf("%s\n", pMenuItem->GetDescription().c_str());
        }
    
        delete pPancakeHouseMenu;
        delete pDiningRoomMenu;
        return 0;
    }
    

    运行结果没问题

    A Pancake - 2.99 - Pancakes with scrambled eggs and toast
    B Pancake - 2.99 - Pancakes with fried eggs and sausage
    C Pancake - 3.49 - Pancakes with fresh blueberries
    D Pancake - 3.59 - Waffles with strawberries
    A Dinner - 2.99 - Vegetarian bacon with lettuce & tomato on whole wheat
    B Dinner - 2.99 - Bacon with lettuce & tomato on whole wheat
    C Dinner - 3.29 - Soup of the day, with a side of potato salad
    D Dinner - 3.05 - Hotdog with saurkraut topped with cheese
    

    显然,我们是针对 PancakeHouseMenu 和 DiningRoomMenu 的具体实现编程,而不是针对接口。
    客户代码需要知道每个菜单如何表达内部的菜单项集合,这违反了封装。
    如果还有第三家餐厅以不同的实现出现,我们就需要有三个循环!

    思考改进

    餐厅和煎饼屋的代码现状让我们很为难,他们都不想改变自身的实现,因为意味着要重写很多代码。这就导致了我们写出来的客户代码将难以维护、难以扩展。
    在这里插入图片描述

    还记得封装变化吗?
    很显然,在这里发生变化的是:由不同的集合类型所造成的遍历。
    但是,这个也能被封装吗?我们来看看这个想法…

    现在,我们创建一个对象,称之为迭代器 (Iterator),利用它来封装遍历集合内的每一个对象的过程
    先在 vector 上试试:

    Iterator *it = pancakeHouseMenu->CreateIterator();
    while(it->HasNext()) {
        MenuItem *menuItem = it->Next();
    }
    

    客户代码只需要调用 HasNext() 和 Next(),而迭代器会暗中调用 vector 的 size() 和 at() 方法

    在数组上也试试:

    Iterator *it = diningRoomMenu->CreateIterator();
    while(it->HasNext()) {
        MenuItem *menuItem = it->Next();
    }
    

    这个代码和上面的 pancakeHouseMenu 代码完全一样。客户代码只需调用 HasNext() 和 Next(),而迭代器会暗中使用数组的下标。

    Version 2.0

    首先,我们需要一个迭代器的接口,代码如下
    加上了模板,避免使用 void*,客户代码就不用做类型强转了!

    template<class Item>
    class IIterator {
    public:
        virtual bool HasNext(void) = 0;
        virtual Item *Next(void) = 0;
    };
    

    有了迭代器接口,就可以为各种集合(数组、vector等)实现迭代器(C++已经为 vector 实现了迭代器,在此为了演示,自行实现一个)
    PancakeHouseIterator 实现了迭代器接口,是一个具体的迭代器,知道如何遍历 vector 菜单项

    class PancakeHouseIterator : public IIterator<MenuItem> {
    public:
        PancakeHouseIterator(vector<MenuItem *> items) {
            m_nPosition = 0;
            m_aItems = items;
        }
        bool HasNext(void) {
            if(m_nPosition >= m_aItems.size()) {
                return false;
            } else {
                return true;
            }
        }
        MenuItem *Next(void) {
            MenuItem *pItem = m_aItems.at(m_nPosition);
            m_nPosition++;
            return pItem;
        }
    private:
        int m_nPosition;
        vector<MenuItem *> m_aItems;
    };
    

    同理,DiningRoomIterator 也实现了迭代器接口,知道如何遍历数组菜单项

    class DiningRoomIterator : public IIterator<MenuItem> {
    public:
        DiningRoomIterator(MenuItem *items[], int size) {
            m_nPosition = 0;
            m_nSize = size;
            m_aItems = items;
        }
        bool HasNext(void) {
            if(m_nPosition >= m_nSize || m_aItems[m_nPosition] == NULL) {
                return false;
            } else {
                return true;
            }
        }
        MenuItem *Next(void) {
            MenuItem *pItem = m_aItems[m_nPosition];
            m_nPosition++;
            return pItem;
        }
    private:
        int m_nPosition;
        int m_nSize;
        MenuItem **m_aItems;
    };
    

    有了两个具体的迭代器,就利用它们来改写餐厅和煎饼屋菜单
    我们只需加入一个 CreateIterator() 方法来创建具体的迭代器返回给客户代码即可
    注意:我们不再需要 Version1.0 的 GetMenuItems() 方法了,因为它会暴露菜单内部的实现,删掉该方法

    class PancakeHouseMenu {
    public:
        ......
        IIterator<MenuItem> *CreateIterator(void) {
            return new PancakeHouseIterator(m_aMenuItems);
        }
    private:
        vector<MenuItem *> m_aMenuItems; // 容器的形式
    };
    

    CreateIterator() 返回迭代器接口,客户代码不需要知道煎饼屋菜单是如何维护菜单的,也不需要知道迭代器是如何实现的,客户只需直接使用这个迭代器遍历菜单项即可。

    同样,餐厅菜单也做一样的修改,去掉 GetMenuItems() 方法,添加 CreateIterator() 方法

    class DiningRoomMenu {
    public:
        ......
        IIterator<MenuItem> *CreateIterator(void) {
            return new DiningRoomIterator(m_aMenuItems, m_nMaxItems);
        }
    private:
        const static int m_nMaxItems = 6;
        int m_nNumberOfItems;
        MenuItem *m_aMenuItems[m_nMaxItems]; // 数组的形式
    };
    

    好了,最后来看看客户代码怎么写

    // 现在我们只需要一个循环就可以
    void PrintMenu(IIterator<MenuItem> *it) {
        while(it->HasNext()) {
            MenuItem *pMenuItem = it->Next();
            printf("%s - ", pMenuItem->GetName().c_str());
            printf("%.2f - ", pMenuItem->GetPrice());
            printf("%s\n", pMenuItem->GetDescription().c_str());
        }
    }
    int main(int argc, char *argv[]) {
        // 客户需要创建两个菜单
        PancakeHouseMenu *pPancakeHouseMenu = new PancakeHouseMenu();
        DiningRoomMenu *pDiningRoomMenu = new DiningRoomMenu();
        // 客户只需要创建两个迭代器
        IIterator<MenuItem> *pPancakeIterator = pPancakeHouseMenu->CreateIterator();
        IIterator<MenuItem> *pDiningIterator  = pDiningRoomMenu->CreateIterator();
        // 使用迭代器来遍历菜单项并打印出来
        PrintMenu(pPancakeIterator);
        PrintMenu(pDiningIterator);
        delete pPancakeHouseMenu;
        delete pDiningRoomMenu;
        return 0;
    }
    

    可见,客户代码不用关心两个菜单内部是用什么方式来存储菜单项的
    运行结果跟 Version1.0 一样

    大功告成,餐厅和煎饼屋他们可以保持自己的实现又可以摆平差别。只要给他们这两个迭代器,他们只需要加入一个 CreateIterator() 方法就可以了。
    同时,客户代码将会更容易维护和扩展。
    在这里插入图片描述

    用类图看看目前的设计
    在这里插入图片描述

    Version 2.1

    有没有发现,Version2.0 的客户代码里,我们还是依赖了 PancakeHouseMenu 和 DiningRoomMenu 这两个具体的菜单,我们得再改进一下
    PancakeHouseMenu 和 DiningRoomMenu 都有 CreateIterator() 方法,可以把它提取出来放在接口里,如 IMenu 接口

    template<class Item>
    class IMenu {
    public:
        virtual IIterator<Item> *CreateIterator(void) = 0;
    };
    

    PancakeHouseMenu 和 DiningRoomMenu 只要实现 IMenu 接口就可以了

    class PancakeHouseMenu : public IMenu<MenuItem> {
    ......
    };
    
    class DiningRoomMenu : public IMenu<MenuItem> {
    ......
    };
    

    客户代码只需要将原来的具体菜单类改成 IMenu 接口就可以了

    int main(int argc, char *argv[]) {
        // 客户需要创建两个菜单
        IMenu<MenuItem> *pPancakeHouseMenu = new PancakeHouseMenu();
        IMenu<MenuItem> *pDiningRoomMenu = new DiningRoomMenu();
        ......
    }
    

    改进后的类图如下
    在这里插入图片描述

    清爽了许多,客户代码利用接口(而不是具体类)引用每一个菜单对象。通过针对接口编程,而不是针对实现编程,我们就可以减少客户代码和具体类之间的依赖。

    迭代器模式定义

    现在,我们来说下什么是迭代器模式?
    迭代器模式,属于行为型模式的一种。它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露聚合对象的内部表示。
    迭代器模式就是分离了聚合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露聚合的内部结构,又可以让外部代码透明地访问聚合内部的数据。

    迭代器模式的优缺点

    无论哪种模式都有其优缺点,当然我们每次在编写代码的时候需要考虑下其利弊
    迭代器模式的优点:

    1. 简化了遍历方式。用户不需要了解聚合内部结构就可以遍历
    2. 可以提供多种遍历方式。比如说对有序列表,我们可以根据需要提供正序遍历、倒序遍历两种迭代器
    3. 封装性良好。用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心
    4. 迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历的方法,这样可以简化聚合类的设计

    迭代器模式的缺点:

    1. 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
    2. 对于比较简单的遍历(像数组或者有序列表),使用迭代器方式遍历较为繁琐

    总结

    当你需要访问一个集合对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式。
    迭代器模式是与集合同生共死的,一般来说,只要实现一个集合,就需要同时提供这个集合的迭代器,就像 C++ 中的vector、map 等,这些集合都有自己的迭代器。
    但是,由于集合与迭代器的关系太密切了,所以大多数编程语言在实现容器的时候都提供了迭代器,在绝大多数情况下就可以满足我们的需要。所以现在需要我们自己去实现迭代器模式的场景还是比较少见的。
    在这里插入图片描述

    参考资料

    https://blog.csdn.net/u012611878/article/details/78010435

    https://blog.csdn.net/wwwdc1012/article/details/83020422

    Head+First设计模式(中文版).pdf

    展开全文
  • 《Head first设计模式》学习笔记 – 迭代模式 代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。 爆炸性新闻:对象村餐厅和对象村煎饼屋合并了!真是个好消息!现在我们可以...
  • 设计模式学习专栏九--------迭代模式 名称 : 迭代模式(Iterator) 价值观念: 管理良好的集合 场景 爆炸性新闻: 对象村餐厅和煎饼屋餐厅屋合并了 , 我们打算用煎饼屋的菜单当早餐 , 用对象村餐厅的菜单当中餐. ...
  • 15.迭代模式(Iterator)在软件开发中,经常需要将某一类对象放在一个集合里,或者放在容器里,这个时候通常需要对集合或容器里的对象进行访问,很明显,对集合或容器里对象的访问必须涉及遍历,这就是迭代模式...
  • 五天实现CSDN新闻MIS系统开发手记

    千次阅读 2005-08-31 01:02:00
    而本文详细记录了我在使用轻量级B/S引擎NoahWeb来开发B/S结构的CSDN新闻MIS系统的时所使用的“增量迭代”开发模式的过程。意在让更多的人能了解和对比NoahWeb中所倡导的“增量迭代”开发模式同其他模式的不同之处。 ...
  • 主要内容包括如何将敏捷团队从基础工具环境搭建中解脱出来、在迭代中追求“业务价值”和“获取知识”、如何评价一个人是否适合XP?、丰富的设计技能胜过特定于平台的知识、InfoQ中文站翻译之作《实现模式》中文版...
  • 进入移动互联网成熟阶段,技术迭代模式更新,亦容易带来内容的野蛮生长。在此情况下,媒体的定位及价值坚守究竟是什么,用户,流量,还是内容? 在不久前,工信部情报所网络舆情研究中心发布了一份《2017中国网络...
  • 简阅是我学习安卓开发的第一个项目,最初是使用传统的MVC模式,然后一步步迭代,由MVP再到Kotlin.如今项目功能已经基本稳定,我将项目规范了下, 然后开源供大家交流学习,毕竟当时学习也看了很多前辈的项目,学到了很多,...
  • 我们只会在发布TIGRE的新版本或有重大新闻时与您联系。 TIGRE仍在开发中,我们仍在为其添加新功能。 如果您对您的特定应用有任何要求,请随时与我们联系! 有关TIGRE,迭代算法和GPU计算等的详细说明。 下载: 消息 ...
  • iOS-项目配置(新项目)

    2019-10-09 07:21:54
    1.App类型:社交、电商、视频、娱乐、新闻 2.App开发模式迭代开发(原有基础上开发) ②独立开发 项目开发方式:①storyboard ②纯代码 其他:需求文档、原型图、接口文档、开发设计文档 3.Target-General配置...
  • PET的迭代变体(iPET)可以训练多代模型,甚至可以在没有任何训练数据的情况下使用。 #例子 训练模式 Yelp(完整版) AG新闻 雅虎问题 MNLI 0 无监督 33.8 69.5 44.0 39.1 iPET 56.7 87.5 70.7 53.6 ...
  • Xcode 配件之 InjectionIII

    2019-05-27 15:19:55
    想过分主体开发,但现有的模式改动成本有点大,版本迭代周期短,先寻求额外的插件来稍微弥补一下即可。 在浏览戴铭大师的专栏时,看到一个方案,实践并记录一下。 实验 1 环境 macOS Mojave 10.14.5 + Xcode ...
  • 所有的 Boost 库文档的索引

    千次阅读 2017-04-20 23:22:27
    入门 工具 网站 新闻 社区 常见问题 更多的信息 按字母顺序列出的库 按类别列出的库 算法 破碎的编译器的解决方法 并发编程 容器 正确性和测试 数据结构 特定于域的 函数对象和高阶编程 泛型编程 图像...
  • 计算技术演变历程: 1、离线计算Mapreduce:...3、实时计算Spark:满足海量数据的深度挖掘中机器学习/模式识别的迭代型算法,每次计算结果分布式的放在内存中,下一轮直接从内存中读取上一轮的数据,节省大量的IO...
  • 案例1:新浪的发展历程(八字原则:快速、全面、准确、客观)(名人战略,名人+草根模式)全面整合传统媒体的内容的能力,以及应对突发事件的快速反应能力。新浪微博为互联网行业公认运营能力最强的产品。拥有优秀的...
  • 在5G、AI、云计算、大数据、物联网等新兴技术迭代更新的驱动下,智慧城市建设持续加速,以创新的理念+科技手段推动监管创新、服务创新成为城市精细化管理和运营的重点方向。城市管理执法也逐渐向 “互联网+”发展...
  • 这是Angular 2的基本模板。它包含启动Angular 2项目时所需的核心文件。 要开始使用,请按照以下说明进行操作。 安装Node.js和npm 如果尚未在计算机上安装最新版本的... -Subreddit,用于Google的下一个AngularJS迭代

空空如也

空空如也

1 2 3 4
收藏数 71
精华内容 28
关键字:

迭代新闻模式