用C# (.NET Core) 实现迭代器设计模式

本文的概念来自深入浅出设计模式一书

项目需求

有两个饭店合并了, 它们各自有自己的菜单. 饭店合并之后要保留这两份菜单.

这两个菜单是这样的:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

菜单项MenuItem的代码是这样的:

640?wx_fmt=png

最初我们是这样设计的, 这是第一份菜单:

640?wx_fmt=png

这是第2份菜单:

640?wx_fmt=png

同时有两个菜单存在的问题

问题就是多个菜单把事情变复杂了. 例如: 如果一个服务员需要使用两份菜单的话, 那么她就无法很快的告诉客户有哪些菜是适合素食主义者的了.

服务员还有可能有这些需求:

640?wx_fmt=png

打印菜单, 打印早餐菜单, 打印午餐菜单, 打印素食菜单, 判断某个菜是否是素食的.

首先我们尝试一下如何实现打印菜单:

1. 调用两个菜单上面的getMenuItem()方法来获取各自的菜单项, 由于它们的菜单不同, 所以需要写两段代码:

640?wx_fmt=png

2. 打印两个菜单的菜单项, 同样也是两套代码:

640?wx_fmt=png

3. 如果还有一份菜单, 那么就需要写三套代码....

现在就很麻烦了. 

怎么解决这个问题

 如果能找到一种方式让这两个菜单同时实现一个接口就好了. 我们已经知道, 要把变化的部分封装起来.

什么是变化的部分? 由于不同对象集合引起的遍历操作.

那我们试试;

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

640?wx_fmt=png

2. 想要遍历午餐项, 我们需要使用Array的length成员变量以及通过索引访问数组:

640?wx_fmt=png

3. 如果我们创建一个对象, 把它叫做迭代器, 让它来封装我们遍历集合的方式怎么样?

640?wx_fmt=png

这里, 我们需要早餐菜单创建一个迭代器, 如果还有剩余的菜单项没有遍历完, 就获取下一个菜单项.

4. 让我们在Array上试试:

640?wx_fmt=png

初识迭代器模式

首先你需要知道这种模式依赖于一个迭代器接口. 例如这个:

640?wx_fmt=png

hasNext()方法告诉我们集合中是否还有剩余的条目没有遍历到.

next()方法返回下一个条目.

有了这个接口, 我们可以在任何一种集合上实现该接口.:

640?wx_fmt=png

修改代码

定义迭代器接口:

640?wx_fmt=png

然后再DinerMenu上实现迭代器接口:

640?wx_fmt=png

然后使用迭代器来修改DinerMenu菜单:

640?wx_fmt=png

注意: 不要直接返回集合, 因为这样会暴露内部实现.

createIterator()方法返回的是迭代器的接口, 客户并不需要知道DinerMenu是如何维护菜单项的, 也不需要DinerMenu的迭代器是如何实现的. 它只是用迭代器来遍历菜单里面的条目.

最后服务员的代码如下:

640?wx_fmt=png

测试代码:

640?wx_fmt=png

640?wx_fmt=png

我们做了哪些修改?

我们只是为菜单添加了createIterator()方法.

而现在, 菜单的实现被封装了, 服务员不知道菜单是如何保存菜单项的.

我们所需要的只是一个循环, 它可以多态的处理实现了迭代器接口的集合.

而服务员使用的是迭代器接口.

现在呢, 菜单还没有共同的接口, 这意味着服务员仍然被绑定在两个具体的菜单类上, 一会我们再说这个.

当前的设计图

640?wx_fmt=png

目前就是两个菜单实现了同一套方法, 但是还没有实现同一个接口.

使用C#, .NET Core控制台项目进行实现

菜单项 MenuItem:

namespace IteratorPattern.Menus

{

    public class MenuItem

    {

        public string Name { get; }

        public string Description { get; }

        public bool Vegetarian { get; }

        public double Price { get; }


        public MenuItem(string name, string description, bool vegetarian, double price)

        {

            Name = name;

            Description = description;

            Vegetarian = vegetarian;

            Price = price;

        }

    }

}

迭代器接口 IMyIterator:

namespace IteratorPattern.Abstractions

{

    public interface IMyIterator

    {

        bool HasNext();

        object Next();

    }

}

两个菜单迭代器:

using IteratorPattern.Abstractions;

using IteratorPattern.Menus;


namespace IteratorPattern.MenuIterators

{

    public class MyDinerMenuIterator: IMyIterator

    {

        private readonly MenuItem[] _menuItems;

        private int _position;


        public MyDinerMenuIterator(MenuItem[] menuItems)

        {

            _menuItems = menuItems;

        }


        public bool HasNext()

        {

            if (_position >= _menuItems.Length || _menuItems[_position] == null)

            {

                return false;

            }

            return true;

        }


        public object Next()

        {

            var menuItem = _menuItems[_position];

            _position++;

            return menuItem;

        }

    }

}


using System.Collections;

using IteratorPattern.Abstractions;


namespace IteratorPattern.MenuIterators

{

    public class MyPancakeHouseMenuIterator:IMyIterator

    {

        private readonly ArrayList _menuItems;

        private int _position;


        public MyPancakeHouseMenuIterator(ArrayList menuItems)

        {

            _menuItems = menuItems;

        }


        public bool HasNext()

        {

            if (_position >= _menuItems.Count || _menuItems[_position] == null)

            {

                return false;

            }

            _position++;

            return true;

        }


        public object Next()

        {

            var menuItem = _menuItems[_position];

            _position++;

            return menuItem;

        }

    }

}

两个菜单:

using System;

using System.Collections.Generic;

using System.Text;

using IteratorPattern.Abstractions;

using IteratorPattern.MenuIterators;


namespace IteratorPattern.Menus

{

    public class MyDinerMenu

    {

        private const int MaxItems = 6;

        private int _numberOfItems = 0;

        private MenuItem[] MenuItems { get; }


        public MyDinerMenu()

        {

            MenuItems = new MenuItem[MaxItems];

            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, bool vegetarian, double price)

        {

            var menuItem = new MenuItem(name, description, vegetarian, price);

            if (_numberOfItems >= MaxItems)

            {

                Console.WriteLine("Sorry, menu is full! Can't add item to menu");

            }

            else

            {

                MenuItems[_numberOfItems] = menuItem;

                _numberOfItems++;

            }

        }


        public IMyIterator CreateIterator()

        {

            return new MyDinerMenuIterator(MenuItems);

        }

    }

}


using System.Collections;

using IteratorPattern.Abstractions;

using IteratorPattern.MenuIterators;


namespace IteratorPattern.Menus

{

    public class MyPancakeHouseMenu

    {

        public ArrayList MenuItems { get; }


        public MyPancakeHouseMenu()

        {

            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, 3.49);

            AddItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59);

        }


        public void AddItem(string name, string description, bool vegetarian, double price)

        {

            var menuItem = new MenuItem(name, description, vegetarian, price);

            MenuItems.Add(menuItem);

        }


        public IMyIterator CreateIterator()

        {

            return new MyPancakeHouseMenuIterator(MenuItems);

        }

    }

}

服务员 Waitress:

using System;

using IteratorPattern.Abstractions;

using IteratorPattern.Menus;


namespace IteratorPattern.Waitresses

{

    public class MyWaitress

    {

        private readonly MyPancakeHouseMenu _pancakeHouseMenu;

        private readonly MyDinerMenu _dinerMenu;


        public MyWaitress(MyPancakeHouseMenu pancakeHouseMenu, MyDinerMenu dinerMenu)

        {

            _pancakeHouseMenu = pancakeHouseMenu;

            _dinerMenu = dinerMenu;

        }


        public void PrintMenu()

        {

            var pancakeIterator = _pancakeHouseMenu.CreateIterator();

            var dinerIterator = _dinerMenu.CreateIterator();

            Console.WriteLine("MENU\n--------------\nBREAKFIRST");

            PrintMenu(pancakeIterator);

            Console.WriteLine("\nLUNCH");

            PrintMenu(dinerIterator);

        }


        private void PrintMenu(IMyIterator iterator)

        {

            while (iterator.HasNext())

            {

                var menuItem = iterator.Next() as MenuItem;

                Console.Write($"{menuItem?.Name}, ");

                Console.Write($"{menuItem?.Price} -- ");

                Console.WriteLine($"{menuItem?.Description}");

            }

        }

    }

}

测试:

static void MenuTestDriveUsingMyIterator()

        {

            var pancakeHouseMenu = new MyPancakeHouseMenu();

            var dinerMenu = new MyDinerMenu();


            var waitress = new MyWaitress(pancakeHouseMenu, dinerMenu);

            waitress.PrintMenu();

        }


640?wx_fmt=png

做一些改进

 Java里面内置了Iterator接口, 我们刚才是手写了一个Iterator迭代器接口. Java内置的定义如下:

640?wx_fmt=png

注意里面这个remove()方法, 我们可能不需要它.

remove()方法是可选实现的, 如果你不想让集合有此功能的话, 就应该抛出NotSupportedException(C#的).

使用java内置的Iterator来实现

由于PancakeHouseMenu使用的是ArrayList, 而ArrayList已经实现了该接口, 那么:这样简单改一下就可以:

640?wx_fmt=png

针对DinerMe菜单, 还是需要手动实现的:

640?wx_fmt=png

最后别忘了给菜单规定一个统一的接口:

640?wx_fmt=png

服务员Waitress类里面也使用Menu来代替具体的菜单, 这样也减少了服务员对具体类的依赖(针对接口编程, 而不是具体的实现):

640?wx_fmt=png

最后看下改进后的设计类图:

640?wx_fmt=png

迭代器模式定义

迭代器模式提供了一种访问聚合对象(例如集合)元素的方式, 而且又不暴露该对象的内部表示.

迭代器模式负责遍历该对象的元素, 该项工作由迭代器负责而不是由聚合对象(集合)负责.

类图:

640?wx_fmt=png

其它问题

  • 迭代器分内部迭代器和外部迭代器, 我们上面实现的是外部迭代器. 也就是说客户控制着迭代, 它通过调用next()方法来获取下个元素. 而内部迭代器由迭代器本身自己控制迭代, 这种情况下, 你需要告诉迭代器遍历的时候需要做哪些动作, 所以你得找到一种方式把操作传递进去. 内部迭代器还是不如外部的灵活, 但是也许使用起来会简单一些?

  • 迭代器意味着无序. 它所遍历的集合的顺序是根据集合来定的, 也有可能会遍历出来的元素值会重复.

单一职责设计原则

一个类应该只有一个变化发生的原因.

写代码的时候这个原则很容易被忽略掉, 只能通过多检查设计来避免违反原则.

所谓的高内聚, 就是只这个类是围绕一套关连的函数而设计的.

而低内聚就是只这个类是围绕一些不相关的函数而设计的.

遵循该原则的类通常是高内聚的, 并且可维护性要比那些多重职责或低内聚的类好.

需求变更

还需要添加另一份菜单:

640?wx_fmt=png

这个菜单使用的是HashTable.

首先修改该菜单, 让它实现Menu接口:

640?wx_fmt=png

注意看HashTable的不同之处:

640?wx_fmt=png

首先通过values()方法获取HashTable的集合对象, 这个对象正好实现了Iterator接口, 直接调用iterator()方法即可.

最后修改服务员类:

640?wx_fmt=png

测试:

640?wx_fmt=png

640?wx_fmt=png

 到目前我们做了什么

 我们给了服务员一种简单的方式来遍历菜单项, 不同的菜单实现了同一个迭代器接口, 服务员不需要知道菜单项的实现方法.

 我们把服务员和菜单的实现解耦了

 640?wx_fmt=png

而且使服务员可以扩展:

640?wx_fmt=png

640?wx_fmt=png

还有个问题

现在有三个菜单, 每次再添加一个菜单的时候, 你都得相应的添加一套代码, 这违反了"对修改关闭, 对扩展开放原则".

那我们把这些菜单放到可迭代的集合即可:

640?wx_fmt=png

C#, .NET Core控制台项目实现

菜单接口:

using System.Collections;


namespace IteratorPattern.Abstractions

{

    public interface IMenu

    {

        IEnumerator CreateIEnumerator();

    }

}

三个菜单:

using System;

using System.Collections;

using IteratorPattern.Abstractions;

using IteratorPattern.MenuIterators;


namespace IteratorPattern.Menus

{

    public class DinerMenu: IMenu

    {

        private const int MaxItems = 6;

        private int _numberOfItems = 0;

        private MenuItem[] MenuItems { get; }


        public DinerMenu()

        {

            MenuItems = new MenuItem[MaxItems];

            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, bool vegetarian, double price)

        {

            var menuItem = new MenuItem(name, description, vegetarian, price);

            if (_numberOfItems >= MaxItems)

            {

                Console.WriteLine("Sorry, menu is full! Can't add item to menu");

            }

            else

            {

                MenuItems[_numberOfItems] = menuItem;

                _numberOfItems++;

            }

        }


        public IEnumerator CreateIEnumerator()

        {

            return new DinerMenuIterator(MenuItems);

        }

    }

}


using System.Collections;

using IteratorPattern.Abstractions;

using IteratorPattern.MenuIterators;


namespace IteratorPattern.Menus

{

    public class PancakeHouseMenu: IMenu

    {

        public ArrayList MenuItems { get; }


        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, 3.49);

            AddItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59);

        }


        public void AddItem(string name, string description, bool vegetarian, double price)

        {

            var menuItem = new MenuItem(name, description, vegetarian, price);

            MenuItems.Add(menuItem);

        }


        public IEnumerator CreateIEnumerator()

        {

            return new PancakeHouseMenuIterator(MenuItems);

        }

    }

}


using System.Collections;

using IteratorPattern.Abstractions;


namespace IteratorPattern.Menus

{

    public class CafeMenu : IMenu

    {

        public Hashtable MenuItems { get; } = new Hashtable();


        public CafeMenu()

        {

            AddItem("Veggie Burger and Air Fries", "Veggie burger on a whole wheat bun, lettuce, tomato, and fries", true, 3.99);

            AddItem("Soup of the day", "A cup of the soup of the day, with a side salad", false, 3.69);

            AddItem("Burrito", "A large burrito, with whole pinto beans, salsa, guacamole", true, 4.29);

        }


        public IEnumerator CreateIEnumerator()

        {

            return MenuItems.GetEnumerator();

        }


        public void AddItem(string name, string description, bool vegetarian, double price)

        {

            var menuItem = new MenuItem(name, description, vegetarian, price);

            MenuItems.Add(menuItem.Name, menuItem);

        }


    }

}

菜单的迭代器:

using System;

using System.Collections;

using IteratorPattern.Menus;


namespace IteratorPattern.MenuIterators

{

    public class DinerMenuIterator: IEnumerator

    {

        private readonly MenuItem[] _menuItems;

        private int _position = -1;


        public DinerMenuIterator(MenuItem[] menuItems)

        {

            _menuItems = menuItems;

        }


        public bool MoveNext()

        {

            _position++;

            if (_position >= _menuItems.Length || _menuItems[_position] == null)

            {

                return false;

            }

            return true;

        }


        public void Reset()

        {

            _position = -1;

        }


        public object Current => _menuItems[_position];

    }

}

using System.Collections;

using System.Collections.Generic;


namespace IteratorPattern.MenuIterators

{

    public class PancakeHouseMenuIterator : IEnumerator

    {

        private readonly ArrayList _menuItems;

        private int _position = -1;


        public PancakeHouseMenuIterator(ArrayList menuItems)

        {

            _menuItems = menuItems;

        }


        public bool MoveNext()

        {

            _position++;

            if (_position >= _menuItems.Count || _menuItems[_position] == null)

            {

                return false;

            }

            return true;

        }


        public void Reset()

        {

            _position = -1;

        }


        public object Current => _menuItems[_position];

    }

}

服务员:

using System;

using System.Collections;

using IteratorPattern.Abstractions;

using IteratorPattern.Menus;


namespace IteratorPattern.Waitresses

{

    public class Waitress

    {

        private readonly ArrayList _menus;


        public Waitress(ArrayList menus)

        {

            _menus = menus;

        }


        public void PrintMenu()

        {

            var menuIterator = _menus.GetEnumerator();

            while (menuIterator.MoveNext())

            {

                var menu = menuIterator.Current as IMenu;

                PrintMenu(menu?.CreateIEnumerator());

            }

        }


        private void PrintMenu(IEnumerator iterator)

        {

            while (iterator.MoveNext())

            {

                if (iterator.Current != null)

                {

                    MenuItem menuItem;

                    if (iterator.Current is MenuItem item)

                    {

                        menuItem = item;

                    }

                    else

                    {

                        menuItem = ((DictionaryEntry)iterator.Current).Value as MenuItem;

                    }

                    Console.Write($"{menuItem?.Name}, ");

                    Console.Write($"{menuItem?.Price} -- ");

                    Console.WriteLine($"{menuItem?.Description}");

                }

            }

            Console.WriteLine();

        }

    }

}

测试:

static void MenuTestDriveUsingIEnumerator()

        {

            var pancakeHouseMenu = new PancakeHouseMenu();

            var dinerMenu = new DinerMenu();

            var cafeMenu = new CafeMenu();


            var waitress = new Waitress(new ArrayList(3)

            {

                pancakeHouseMenu, dinerMenu, cafeMenu

            });

            waitress.PrintMenu();

        } 

640?wx_fmt=png 

深入浅出设计模式的C#实现的代码: https://github.com/solenovex/Head-First-Design-Patterns-in-CSharp

这篇先到这, 本章涉及到组合模式, 下篇文章再写.



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P4287-[SHOI2011]双倍回文【PAM】

正题 题目链接:https://www.luogu.com.cn/problem/P4287 题目大意 长度为nnn的字符串。定义wRw^RwR表示字符串www的翻转。 一个双倍回文可以表示为wwRwwRww^Rww^RwwRwwR且这是个回文串。 求最长的子串是双倍回文。 解题思路 我们在构建PAMPAMPAM的时候维护一个halfhalfhal…

2020-09-18

1.处理调用子组件方法时,报错undefined问题 2.var 、const 、let区别 3. curl -X GET --header "Accept: */*" "访问地址" 4.mysql中的日期格式化 5. ant-design-vue-jeecg

低价购买(洛谷 1108)

低价购买(洛谷 1108) 题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购…

.NET Core/.NET之Stream简介

之前写了一篇C#装饰模式的文章用.NET Core实现装饰模式和.NET Core的Stream简介 提到了.NET Core的Stream, 所以这里尽量把Stream介绍全点. (都是书上的内容).NET Core/.NET的Streams首先需要知道, System.IO命名空间是低级I/O功能的大本营.Stream的结构.NET Core里面的Stream主…

P4762-[CERC2014]Virus synthesis【PAM,dp】

正题 题目链接:https://www.luogu.com.cn/problem/P4762 题目大意 长度为nnn的目标串,开始一个空串,可以执行以下操作 在头或者尾加一个字符复制一个该串的逆串放在后面 求最少操作次数。 解题思路 我们可以知道答案肯定是一个回文串然后剩下的暴力…

I - Trade Gym - 100212I 网络流

网络流好题 给出A、B两个点集,A、B之间有边相连,而A和B的内部均无边相连。 题目要求求出最多删除A、B之间的多少边,才能使得A中点的度数至少都为2,B中点的度数也至少都为2。 先求出每个点的度数,从每个点v出发&…

1、Spring简介

1、Spring是什么? Spring是一套用于开发J2EE应用程序的轻量级框架,其两大核心是IOC和AOP,也就是提供一个bean统一管理容器,提供面向切面编程的基础。 2、为什么要用Spring? 1)使用Spring肯定是为了简化开…

【深搜】骑士游历(ssl 1277)

骑士游历 Description 如下图所示有m*n(n,m<15)一个棋盘&#xff0c;在棋盘左下角的A&#xff08;1,1&#xff09;点&#xff0c;有一个中国象棋〈马〉&#xff0c;并约定马走的规则&#xff1a; ①走日字&#xff1b;②只能向右走。 Sample Input &#xff19; &…

P6015-[CSGRound3]游戏【树状数组】

正题 题目链接:https://www.luogu.com.cn/problem/P6015?contestId25945 题目大意 nnn张牌&#xff0c;玩家111从顶拿若干张&#xff0c;之后玩家222拿若干张。 若牌的和大于KKK那么分数为0否则为牌的和。 求KKK为多少时玩家111必胜。 解题思路 我们枚举玩家111拿多少张&a…

修复迁移后Net Standard项目中的错误

接上一章&#xff0c;项目编译结果如下&#xff1a;解决依赖dll引用在Net Framework项目的引用如下&#xff1a;各引用和作用&#xff1a;log4net(1.10.0.0) 用于写框架日志Castle.DynamicProxy(1.1.5.1) 用于代理类生成Micosoft.Practice.EnterpiseLibrary 微软企业库&#xf…

【深搜】骑士游历(二)

骑士游历&#xff08;二&#xff09; 问题描述&#xff1a;设有一个nn的棋盘&#xff08;n≤10&#xff09;&#xff0c;在棋盘上的任意一点A(x,y)有一中国象棋<马>,<马>走的规则同前&#xff0c;但取消<马>只能向右走的条件。试找出一条路径&#xff0c;使…

P3449-[POI2006]PAL-Palindromes【结论题,字符串hash】

正题 题目链接:https://www.luogu.com.cn/problem/P3449 题目大意 nnn个回文串&#xff0c;求有多少对回文串有序拼接可以形成一个新的回文串。 解题思路 结论:当两个回文串的最短循环节相同时两个拼接起来就是一个新的回文串。 这里感性证明一下&#xff1a; 若两个回文串…

迁移Net项目为Net Core\Standard项目

背景&#xff1a;我们公司内部有自己ORM开发框架&#xff0c;最新因为需要将系统迁移到国产服务器上&#xff0c;所以首先需要将最基础的ORM框架改造可以运行在国产服务器上。对于我们Net来说&#xff0c;优选Net Core。在迁移的过程中&#xff0c;将一些经验和坑记录下来&…

2-sat模板- 输出可行解

自己写的代码 using namespace std; const int maxn 1e6; int head[maxn]; int DFN[maxn],LOW[maxn],stk[maxn],visit[maxn],belong[maxn]; vector<int> scc[maxn]; int tot,idx,cnt,sccnum; int n,color[maxn],degree[maxn],pos[maxn]; struct Es{ int v; int next; in…

读从电子到产品

1、用户的量不重要&#xff0c;质更重要&#xff0c;要让用户真正跟你的产品产生很强大的关系&#xff0c;要看解决问是不是够好、够快、够准&#xff0c;在解决问题的时候&#xff0c;要保证产品真正能满足用户的需求。 2、你认为的问题&#xff0c;别人未必觉得是问题&#…

迁移后的Net Standard版本的类库测试和多平台项目实测

按照第一步的方法&#xff0c;添加一个Net Core的MSTest项目&#xff0c;然后将原来的测试项目下的代码迁移到新测试 项目中&#xff0c;然后引用新的Beyondbit.Framework Core类库。然后运行单元测试项目和集成测试项目即可。测试当中单元测试下项目没有问题&#xff0c;一遍就…

【深搜】01串

01串 Description 用n个0和n个1排成一个2n位的二进制数,要求从最高位起到任意一位,0的个数不能少于1的个数。编程求出所有符合条件的2n位二进制数。 如n&#xff13;时&#xff0c;符合条件的共有5个&#xff1a; &#xff10;&#xff10;&#xff10;&#xff11;&#x…

poj3648 Wedding 2-sat

题目链接&#xff1a; 题目 建图&#xff1a; 1.新娘向新郎连边&#xff0c;表示选取的时候选取新娘对面的。 2. 有奸情的人比如是 xh yh那么连边(x,yn)以及(y,xn) 如果是xh yw 那么连边(x,y) 以及 (yn,x) 如果是xw yh 那么连边(xn,yn)以及(y,x) 如果是xw yw那么连边(x…

P3573-[POI2014]RAJ-Rally【拓扑排序,二分+树状数组】

正题 题目链接:https://www.luogu.com.cn/problem/P3573 题目大意 nnn个点mmm条边的DAGDAGDAG&#xff0c;删掉一个点使得最长路最短。 解题思路 先跑一遍拓扑排序 dsids_idsi​表示以iii结尾的最长路&#xff0c;dtidt_idti​表示以iii开头的最长路&#xff0c;用拓扑序dp可…

读上瘾-让用户养成习惯

1、凡是成功的创新都有一个共性&#xff1a;能够解决问题&#xff0c;看似明确&#xff0c;实则复杂&#xff0c;因为人们总是对新产品应该解决何种问题各执一词。 3、产品不只是满足功能&#xff0c;还要反映人性。好的产品是一件作品&#xff0c;好的技术几近于艺术&#xf…