数据结构和业务操作

需求

新闻标签是发布新闻的用户可以给新闻添加的说明数据,假设用户可添加1~3个标签,包括自定义标签和系统标签。系统标签是应用预定义的标签,供用户选择。用户也可以自己输入自定义标签。

数据定义

  • 标签分类:
    标签的类别。系统标签都有它的分类,比如“体育”、“电影”...等。
    自定义标签属于“自定义”分类。
  • 标签:
    用于对新闻进行附加说明的“文本内容”,例如说明新闻的核心内容的关键字,话题细分标题。

数据抽象

模型类

从oop的角度,分别设计TagCategory、Tag两个类。

class TagCategory {
    String name;
    int type;
    int id;
}

class Tag {
    String name;
    int type;
    int id;
}

如果认为"标题分类"、“标签”可以简单到仅仅是一个String,那么好像没有必要设计TagCategory、Tag类。但从业务发展上,这样很容易陷入误区:
假设系统标签针对不同用户、不同类型用户是不同的,在未来,某些情况下,又会变成很可能存在不同的标签分类、或标签的名称相同这样的情况。
所以,我们总是设计类拥有id,它表示一个业务无关的唯一性,对应一个“数据实体”,类似于数据库中的一条记录就是一个实体。在Java中,不同对象具有相同id那么就是相同的记录。
而更多业务方面的“相等性”是根据和业务人员商定下来的模型的其它字段共同决定的。

另一方面,假设用String来表示分类和标签——那么有关分类和标签的数据都是体现在对应变量的名称上,而没有任何类型信息:

ArrayList<String> mTagCategories;
ArrayList<String> mAllTags;
HashMap<String, ArrayList<String>> mTagData;

上面的数据结构都是基本类型,这意味着我们无法为期望的数据模型增加额外的功能——为类增加方法那样。

class TagCategory {
    String name;
    int type;
    int id;
    ArrayList<String> tags;
}

class Tag {
    String name;
    int type;
    int id;
    TagCategory category;
}

如果是面向对象的定义,标签分类和标签应该像上面那样。

分类和标签的关联:数据库和OOP的不同方式。

OOP和关系型数据库是两种不同的设计思路,想象下多少ORM做的事情:对象关系映射,就是把表之间的记录的1对1、1对多、多对1关系转变为对象之间的引用。
像Tag在数据库里,需要使用categoryId来表示关联的分类,而在Tag对象,直接使用字段存储指向对应TagCategory对象的引用即可。
一些情况下:我们得到Tag对象的时候,并没有TagCategory对象存在——也不关心它的使用,那么从Tag对象数据的晚自性上看,它还是需要一个int categoryId来像数据库表的设计那样,存储着对应TagCategory的信息。

事情还有最后的解决方案:
类似数据库中“用户”、“权限”表那样,使用一个额外的表来存储用户和权限之间的关联。
可以让TagCategory的List<Tag> tags来仅单向存储TagCategory包含的Tag集合,而根据Tag找到对应分类的操作需要额外的方法来完成。Tag中是否保存对应的TagCategory的信息? 如果Tag数据经常出现在无对应TagCategory的上下文中,而又需要知道它的TagCategory,那么就必须存储对应的
categoryId了。

业务操作

对应服务器返回的系统标签数据集合,我们在使用的时候有若干方式:
1. 得到标签分类的列表。
2. 得到某个标签分类下的标签列表。
3. 对于任意的标签,得到它的分类。
4. 得到所有标签的集合。
5. 根据标签id找到标签。

这里涉及到列表和查询。
一般的,我们从服务器返回的json数据是一个个嵌套的Map对象。

{
"newsTags":[
        {
            "categoryName":"分类1",
            "tagList":[
                {
                    "tagName":"标签1",
                    "tagId":"1",
                    "tagType":"hot"
                },
                {
                    "tagName":"标签2",
                    "tagId":"2",
                    "tagType":"spot"
                }
            ]
        },
        ...
    ]
}

上面的数据结构对应返回完整的具备“分类”的标签数据是非常合适的。
在解析完成之后,需要形成TagCategory的List,每个TagCategory对象本身包含它拥有的Tag的List。

作为“字典”数据,如果系统中很多操作都需要根据以上的分类、标签数据来完成一些查询操作,如3和5,如果我们拥有List<TagCategory>List<Tag>,那么通过遍历列表可以完成任务。
问题是当查找操作很频繁,那么每次都去循环就很低效。
可以对List进行排序,然后提升查找的效率。

标签数据量不大时,可以使用Map来存储标签的id及其对应的Tag对象,这样通过id找到Tag对象就很高效了,List和Map前者注重顺序,后者注重查询。

假设我们有300个系统标签,现在有10个tagId,需要转换为对应的Tag对象,使用Map要远比查找List高效的多。
所以,在业务操作不同时,根据数据规模适当选择合适的数据结构来存储数据。

待考量

假设西瓜重量在12-30斤,一个桶100斤,设计算法,对应给定的100个西瓜,使用最少的桶装完。

这个的实际应用是:
标签云在显示的时候需要自动换行,如果希望标签的排列紧凑一些,怎么用最少的行数显示所有标签?

App里需要维护全局唯一的一份数据,此数据第一次使用加载,没有任何地方使用时自动释放内存?

类的对象有默认值,同样的,数据库存储的Tag,它的type值不一样时是不同的记录,那么需要一个唯一id去记录,但是数据在app中是不考虑这个id的,如何和服务器确定返回给我们的数据模板和最终上传的数据项。