一、 引言

Ruby on Rails仅是使Ruby成为伟大的一个因素,这就象EJB仅是Java企业平台的一个组成部分一样。本文将带你一同讨论,作为一名Java开发者,仅利用Ruby自身所能够实现的功能。

首先让我先澄清一些问题。第一,这不是一篇关于Ruby on

Rails的文章。如果你想了解Rails,那么每周都出版新的文章和博客来颂扬这种令人激动的框架及其各种特征。第二,本文并非在预告,在目前出现一些

更好的语言、工具和框架(如Ruby on Rails)时,Java平台即将崩溃。因此,本文与最近有关Ruby的主题并无多大关系。

别误会我-在我看来,Rails还是相当绝妙的!它是如此惊人地有力以致明显地影响了Web开发的许多方面。我的唯一看法是,有更多的关于Ruby而不是Rails的东西,特别是当从一个Java开发者的角度来看问题时。

Rails的特长是网站开发;然而,我发现自己在构建网站时并没有经常使用这种技术。我所构建的大多数网站主要使用了Struts,Tapestry或

其它一些技术。当我利用Ruby时,我仅把它作为开发实践的一部分来使用。因此,在本文中我将讨论,如果你主要是一位Java开发者,那么如何用Ruby

来进行开发。

二、 初步感觉

Ruby的语法与Java语言存在明显区别。首先,Ruby没有括号或分号,并且它的类型完全是可选的。一些人可能说Ruby的语法相当精炼,并且它的目的之一就是用短命令编写简明的代码。

通过比较实现一个功能相对完善的类你就可以体会到这一点,在本文中我们先用Java语言定义它,然后再用Ruby来实现。本文中我先使用两个类:Word和Definition。在图1的简单类图中,你可以看到两个类共享一些关系:

·一个Word拥有一个同义词(也称作Word实例)集合。

·一个Word还可以拥有一个Definition集合。

·一个Definition有一个到Word的聚合关联(aggregation association)。

图1.一本含有单词及其定义的简单字典的类图

三、 在Java语言中的类定义

在列表1中,我用Java语言定义了Word类。请注意,我必须实现我的集合中Definition和同义词的关系确认。这是必要的,因为在这个示例

中,Definition的创建不需要用一个Word关系来初始化,而Word也可以在不使用Definition初始化的情况下定义。

列表1.用Java语言实现的一个类Word

package com.vanward.dictionary;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Iterator;

public class Word {

private String spelling;

private String partOfSpeech;

private Collection definitions;

private Collection synonyms;

public Word(String spelling, String partOfSpeech) {

this.spelling = spelling;

this.partOfSpeech = partOfSpeech;

this.definitions = new ArrayList();

this.synonyms = new ArrayList();

}

public Word(String spelling, String partOfSpeech, Collection definitions) {

this(spelling, partOfSpeech);

if(definitions != null){

for(Iterator iter = definitions.iterator(); iter.hasNext();){

this.validateRelationship((Definition)iter.next());

}

this.definitions = definitions;

}

}

public Word(String spelling, String partOfSpeech, Collection definitions, Collection synonyms) {

this(spelling, partOfSpeech, definitions);

if(synonyms != null){

this.synonyms = synonyms;

}

}

private void validateRelationship(Definition def){

if(def.getWord() == null || def.getWord() != this){

def.setWord(this);

}

}

public Collection getDefinitions() {

return definitions;

}

public void addDefinition(Definition definition) {

this.validateRelationship(definition);

this.definitions.add(definition);

}

public String getPartOfSpeech() {

return partOfSpeech;

}

public void setPartOfSpeech(String partOfSpeech) {

this.partOfSpeech = partOfSpeech;

}

public String getSpelling() {

return spelling;

}

public void setSpelling(String spelling) {

this.spelling = spelling;

}

public Collection getSynonyms() {

return synonyms;

}

public void addSynonym(Word synonym) {

this.synonyms.add(synonym);

}

}

列表1中的Word类相当简单-它是一个JavaBean,它有一个构造器链允许用户用各种属性集来创建Word。还要注意,它的synonyms和

definitions属性都被设置为只读的(也就是说,它们没有相应的setter方法)。你只能为一个同义词添加一个Definition或另一个

Word实例。

在列表2中,你将看到相关的Definition类,它类似于Word类-它的exampleSentences属性也没有一个相应的set()方法:

列表2.用Java语言实现的一个Definition类

package com.vanward.dictionary;

import java.util.Collection;

public class Definition {

private Word word;

private String definition;

private Collection exampleSentences;

public Definition(String definition){

this.definition = definition;

this.exampleSentences = new ArrayList();

}

public Definition(String definition, Word word) {

this(definition);

this.word = word;

}

public Definition(String definition, Word word,Collection exampleSentences) {

this(definition, word);

if(exampleSentences != null){

this.exampleSentences = exampleSentences;

}

}

public String getDefinition() {

return definition;

}

public void setDefinition(String definition) {

this.definition = definition;

}

public Collection getExampleSentences() {

return exampleSentences;

}

public void addExampleSentence(String exampleSentence) {

this.exampleSentences.add(exampleSentence);

}

public Word getWord() {

return word;

}

public void setWord(Word word) {

this.word = word;

}

}

四、 在Ruby中定义类

在列表3中,你可以看见以Ruby定义的两个相同的类;然而,列表3看上去与上面相当不同,对不对?

列表3.用Ruby定义的两个与前面功能相同的类

module Dictionary

class Word

attr_reader :spelling, :part_of_speech, :definitions, :synonyms

attr_writer :spelling, :part_of_speech

def initialize(spelling, part_of_speech, definitions = [], synonyms = [])

@spelling = spelling

@part_of_speech = part_of_speech

definitions.each{ |idef| idef.word = self}

@definitions = definitions

@synonyms = synonyms

end

def add_definition(definition)

definition.word = self if definition.word != self

@definitions << definition

end

def add_synonym(synonym)

@synonyms << synonym

end

end

class Definition

attr_reader :definition, :word, :example_sentences

attr_writer :definition, :word

def initialize(definition, word = nil, example_sentences = [])

@definition = definition

@word = word

@example_sentences = example_sentences

end

end

end

如果说列表3中有一件事情值得你注意的话,那就是,Ruby的语法相当简练。但是,别让它的简练愚弄了你-在这些代码中存在很多内容!首先,两个类都被

定义在一个模块中。实质上,模块相当于Java语言的一个包。而且,在Java语言中,我可以据需要把这些类在多个文件中定义。你还要注意,Ruby中的

类构造器被命名为initialize,而在Java语言中,构造器是使用类名命名的。

五、 快速移动对象

在Ruby中,创建对象实例的方法与Java不同。不是使用Java中的"new

ObjectInstance()"语法,而是,Ruby支持在一个对象上调用一个新方法,实际上是在内部调用了initialize方法。在列表4中,

你可以看到我是怎样用Ruby创建一个Word实例及一些相应的Definition的:

列表4.在Ruby中创建一个新对象实例

require "dictionary"

happy_wrd = Dictionary::Word.new("ebullient", "adjective")

defin_one = Dictionary::Definition.new("Overflowing with enthusiasm")

defin_two = Dictionary::Definition.new("Boiling up or over")

happy_wrd.add_definition(defin_one)

happy_wrd.add_definition(defin_two)

在列表4中,我使用Ruby的require方法(你可以在Kernel类中找到它)导入了dictionary模块。然后,它使用

"Object.new"语法创建Word的一个新实例(ebullient)。尽管我导入了dictionary模块,但是我仍然需要限定对象实例,因

此我们使用了"Dictionary::Word"限定符。

六、 默认参数值

你是否注意到,在列

表4中,当我创建happy_wrd实例时,我并没有一个definition或synonym的集合?我仅传递了拼写和语法成份。因为Ruby支持参数

默认值,所以我成功地进行了省略。在列表3中定义的Word的initialize方法中,我指定了定义"=[]"和同义词"=[]"作为参数,这也就是

向Ruby指出,如果它们不被调用者包括在内,那么将把它们默认地指定为空集合。

还要注意,在列表3中,Definition的

initialize方法是如何支持默认参数的-通过把example_sentences设置为一个空集(Ruby的nil相应于Java中的

null)来实现。在前面的列表1中,我必须用Java语言创建三个构造器才能取得与此相同的灵活性!

现在,请注意,在列表5中,通过使用灵活的initialize()方法我创建了一个不同的Word实例。

列表5.Ruby的灵活性!

require "dictionary"

defin = Dictionary::Definition.new("Skill in or performance of tricks")

defin_two = Dictionary::Definition.new("sleight of hand")

defs = [defin, defin_two]

tricky_wrd = Dictionary::Word.new("prestidigitation", "noun", defs)

在我定义了两个Definition以后,我把它们添加到一个集合(在Java语言中,就象一个数组)。然后,我把该集合传递给Word的initialize()方法。

七、 集合运算

Ruby的集合运算能力也相当简单-你是否看到在Word类中的add_definition和add_synonym方法?<

载意味着可以对集合进行加法运算。如果你看一下前面的列表2中的Definition类,你就会看到Java语言的相应代码更为复杂些:

this.exampleSentences.add(exampleSentence)

Ruby的集合运算极其简明。在列表6中,你可以看到合并集合(使用+操作符)和存取成员(经由[position])是多么容易,这样做你不需要担心任何事情。

列表6.快速的集合运算

require "dictionary"

idef_1 = Dictionary::Definition.new("Sad and lonely because deserted")

idef_2 = Dictionary::Definition.new("Bereft; forsaken")

defs = [idef_1, idef_2]

idef_3 = Dictionary::Definition.new("Wretched in appearance or condition")

idef_4 = Dictionary::Definition.new("Almost hopeless; desperate")

defs_2 = [idef_3, idef_4]

n_def = defs + defs_2 #n_def现在是[idef_1,idef_2,idef_3,idef_4]

n_def[1] #生成idef_2

n_def[9] #生成nil

n_def[1..2] #生成[idef_2,idef_3]

注意,列表6中的代码仅涉及到Ruby中集合运算的基本内容!

八、 RubyBeans?

你可能已经从列表3的两个加粗的类中注意到,Ruby支持使用速记符号来定义属性:它们是attr_reader和attr_writer。因为我使用了这种符号,所以我可以set和get在我的Word类中的相应的属性,见列表7:

列表7.使用attr_reader和attr_writer

require "dictionary"

wrd = Dictionary::Word.new("turpitude", "Noun")

wrd.part_of_speech # "Noun"

wrd.spelling # "turpitude"

wrd.spelling = "bibulous"

wrd.spelling # "bibulous"

syns = [Dictionary::Word.new("absorptive", "Adjective"),

Dictionary::Word.new("imbibing", "Noun") ]

#危险!

wrd.synonyms = syns = syns #出现错误提示-"Exception: undefined method `synonyms='..."

attr_reader和attr_writer都不是关键字,而实际上都是Ruby中的方法(可以在Module类中找到它们)-它们使用符号作参数。一个符号是以冒号(:)开头的任何变量,甚至在有些情况下,符号本身就是对象!

注意,因为我在列表3中使synonyms成为只读的,所以Ruby否定了我在列表7中最后一行代码的尝试。另外,我可以使用attr_accessor方法编写属性声明代码以指出一个属性既可读也可写。

九、 迭代

灵活的迭代是Ruby编程的快乐之一。请参考列表8,其中列出的是Word的initialize()方法的代码:

列表8.Closures非常方便

def initialize(spelling, part_of_speech, definitions = [], synonyms = [])

@spelling = spelling

@part_of_speech = part_of_speech

definitions.each{ |idef| idef.word = self}

@definitions = definitions

@synonyms = synonyms

end

列表8中的第四行代码中出现了一些不同的东西。对于入门者来说,当我调用definitions实例上的每个方法时,我使用了大括号。每个方法实质上类

似Java语言中的一个Iterator,但是它更简单。在列表8中,由每个方法处理迭代细节而只需调用者专注于期望的效果。在此例中,我传递了一个块,

其意思是"for each value in the collection",也就是idef,它是Definition的一个实例。

列表9实质上展示了用Java语言实现的同一行代码(来自于Word的构造器中,见列表1):

列表9.前面Ruby的每个方法都类似Java的迭代算子

for(Iterator iter = definitions.iterator(); iter.hasNext();){

this.validateRelationship((Definition)iter.next());

}

现在,我要承认Java

5的针对于for循环语法的泛型和new比列表9中要简洁得多。Ruby支持我们熟悉的Java中的循环结构,如for和while;实际上,在Ruby

中,它们是很少使用的,因为在Ruby中的大多数东西都支持迭代的概念。例如,在列表10中,你会看到实现对于一个文件内容的迭代是多么容易:

列表10.迭代变得非常简单

count = 0

File.open("./src/dictionary.rb").each { |loc| puts "#{count += 1}:" + loc }

Ruby中的支持each方法的任何类(如File)都可以让你用这样方式进行迭代。顺便说一下,Ruby的puts方法(见列表10)与Java语言中的System.out.println一样。

十、 条件语句

现在,让我们仔细地分析一下在列表3中的Word类中的一个条件语句。在列表11中,我列出了add_definition()方法的代码:

列表11.巧妙的条件表达

def add_definition(definition)

definition.word = self if definition.word != self

@definitions << definition

end

请仔细观察在第二行中的代码,看一下if语句是怎样跟随表达式的。你当然可以以正常方式书写它(由列表12所示),但是是否列表11更好些?

列表12.使用多种方式来表达一个条件

def add_definition(definition)

if definition.word != self

definition.word = self

end

@definitions << definition

end

在Java语言中,如果条件语句体只占一行,那么你可以删除大括号。在Ruby中,如果条件语句体只占一行,那么你可以编写象列表11所示的表达式。还

要注意,同一样的条件也可以表达为"definition.word = self unless definition.word ==

self",在此使用了Ruby的"unless"特征。效果如何?

十一、 多态性

因为Ruby是一种动态类型化语言,所以它不要求接口。其实,接口的力量完全存在于Ruby中,只

是以一种更为灵活的方式存在而已。在Ruby中,有一个被昵称为"duck

typing"的东西,借助于它,在Ruby中的多态性其实成了一种匹配方法名的问题。下面让我们比较一下Ruby和Java语言中的多态性实现。

(一) Java中的多态性

在Java语言中,展示多态性力量的方法之一是,声明一个接口类型并且让其它类型实现这个接口。然后,你可以把实现对象参考为这种接口类型并且调用在这个接口上的任何方法。作为一个例子,在列表13中,我定义了一个简单接口Filter:

列表13.一个简单Java接口

package com.vanward.filter;

public interface Filter {

boolean applyFilter(String value);

}

在列表14中,我定义了一个实现类,叫RegexPackageFilter,它使用一个正规表达式来实现过滤功能:

列表14.RegexPackageFilter实现了Filter

package com.vanward.filter.impl;

import org.apache.oro.text.regex.MalformedPatternException;

import org.apache.oro.text.regex.Pattern;

import org.apache.oro.text.regex.PatternCompiler;

import org.apache.oro.text.regex.PatternMatcher;

import org.apache.oro.text.regex.Perl5Compiler;

import org.apache.oro.text.regex.Perl5Matcher;

import com.vanward.filter.Filter;

public class RegexPackageFilter implements Filter {

private String filterExpression;

private PatternCompiler compiler;

private PatternMatcher matcher;

public RegexPackageFilter() {

this.compiler = new Perl5Compiler();

this.matcher = new Perl5Matcher();

}

public RegexPackageFilter(final String filterExpression){

this();

this.filterExpression = filterExpression;

}

public boolean applyFilter(final String value) {

try{

Pattern pattrn = this.getPattern();

return this.matcher.contains(value, pattrn);

}catch(MalformedPatternException e){

throw new RuntimeException("Regular Expression was uncompilable " +

e.getMessage());

}

}

private Pattern getPattern() throws MalformedPatternException{

return compiler.compile(this.filterExpression);

}

}

现在,让我们设想存在Filter接口的多个实现(例如RegexPackageFilter,一个ClassInclusionFilter类型,也许还有一个SimplePackageFilter类型)。为了实现在程序中的最大灵活性,现在,其它的对象都可以参考这个接口类型(Filter)而不是实现者(implementer),详见列表15:

列表15.功能强大的多态性

private boolean applyFilters(final String value, final Filter[] filters){

boolean passed = false;

for(int x = 0; (x < filters.length && !passed); x++){

passed = filters[x].applyFilter(value);

}

return passed;

}

(二) Ruby中的多态性

在Ruby中,虽然没有接口,但是只要方法名匹配,你就可以使用多态性。

在列表16中,我用Ruby重建了一个Java的Filter类型。注意,每个类都没有联系(除了它们共享相同的方法apply_filter外)。的

确,在实际开发中,应该为这两个类创建一个基类Filter;然而,在此,我想展示在没有类共享的情况下的多态性问题。

列表16.Ruby中的过滤实现

class RegexFilter

attr_reader :fltr_exprs

def initialize(fltr_exprs)

@fltr_exprs = fltr_exprs

end

def apply_filter(value)

value =~ @fltr_exprs

end

end

class SimpleFilter

attr_reader :fltr_exprs

def initialize(fltr_exprs)

@fltr_exprs = fltr_exprs

end

def apply_filter(value)

value.include?(@fltr_exprs)

end

end

注意,在列表16中,我可以通过RegexFilter的apply_filter()方法(经由=~语法)创建一个正规表达式匹配器。(如果你是一位Groovy用户,那么你现在应该很高兴,因为列表16展示了Groovy是如何深深地影响Ruby的)。

十二、 使用duck typing

在列表17中,我使用了Ruby的Test::Unit(它象Java的JUnit一样)来展示duck typing的具体使用。顺便说一下,在Ruby中实现自动化测试就象扩展Test::Unit和添加开始测试的方法一样容易。非常类似于JUnit,对不对?

列表17.使用duck typing技术的过滤实现

require "test/unit"

require "filters"

class FiltersTest < Test::Unit::TestCase

def test_filters

fltrs = [SimpleFilter.new("oo"), RegexFilter.new(/Go+gle/)]

fltrs.each{ | fltr |

assert(fltr.apply_filter("I love to Goooogle"))

}

end

end

注意,在这个test_filters()方法中,我创建了包含两个类(SimpleFilter和RegexFilter)的一个集合。这些类并不共享相同的基类,然而当我遍历这个集合时,我可以很容易地调用apply_filter()方法。

还要注意,Ruby是怎样轻松地实现对正规表达式的支持的。为了创建一个正规表达式,你只需要简单地使用/regex/语法即可。因此,我的列表17中的正规表达式RegexFilter的结果是:一个大写的G,后面跟着一个或多个0,再跟着gle。

十三、 混合(Mix-in)

尽管Ruby中并没有接口,但是它确实提供了一种mix-in。你可以把mix-in当作多重继承使用而免去了多重继承所导致的问题。其实,Mix-

in是一种模块(它不能被实例化)-其中包含的方法可以由一个类选择性包括到该类中。那些模块方法就可以成为包括类的实例方法。

例如,在JUnit中,Assertion类是一个具体的类,它有大量的静态方法,这些都是由TestCase类扩展而来。因此,TestCase的任何实现类都可以在它自己定义的方法内引用一个assert方法。

Ruby的单元测试框架有点不同。不是定义一个Assertion类,而是,它定义了一个Assertions模块。这个模块定义一组

assertion方法,但不是通过扩展(继承),而是,Ruby的TestCase类把assertion作为一个mix-in包括到其中。因此,所有

的那些assert方法现在都成为TestCase上的实例方法,如你在前面的列表17中所见。

十四、 结论

你已看到,Ruby的语法与Java语言存在相当不同,但是使用起来却是惊人地容易。而且,在Ruby中,一些事情实现起来要比在Java中更为简单。

能够使用多种语言编码将会使你面对乏味的和更复杂的编程任务时成为一名多面手,而且,它还能够提高对编程语言的欣赏能力。

如我在本文开始所说的,我主要是一个Java开发者,但是我发现了把Ruby(还有Groovy和Jython……)纳入到我的"知识库"中的好处。现

在,在没有Rails的情况下,我也能实现相应的功能。如果你不想花4个小时时间实现构建一个购物车程序,那么你可以考虑仅用Ruby来实现这个程序,我

想,你一定会非常喜欢你所做的东西。

rails java_没有Rails Java程序员如何使用Ruby相关推荐

  1. 8年java_一个8年Java程序员的年终总结,献给还在迷茫中的你

    恍然间,发现自己在这个行业里已经摸爬滚打了8年了,原以为自己就凭已有的项目经验和工作经历怎么着也应该算得上是一个业内比较资历的人士了,但是今年在换工作的过程中却遭到了重大的挫折. 详细过程我就不再叙述 ...

  2. 赵雅莉个人资料及年龄 java_调查:Java程序员最伤心,C++程序员最年老

    说起我们对编程世界现有的刻板印象,你一定听说过类似于没有人喜欢用Java编码或者使用C++都是老人家,等等这样的话.为了分析这些刻板印象背后的真相,Trestle Technology的数据工程师写了 ...

  3. Java程序员职场全功略 从小工到专家 连载三 IT语言平台

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! IT语言 ...

  4. Stack Overflow : 55.9%的Java程序员想“抛弃”Java

    著名网站StackOverflow每年都会搞一个开发人员的调查,今年有65000名全世界的程序员参与,他们来自于这些地方: 美国和印度的圈圈都很大,加拿大,西欧等国,巴西的圈圈也不小, 中国的圆圈就有 ...

  5. 作为一名合格的JAVA程序员需要点亮那些技能树?

    转自:https://zhuanlan.zhihu.com/p/26035486 先看看这些程序员技能树,你掌握或了解哪些?OMG竟然有么多---震精!震精!!! 这是从450家企业的招聘信息中统计而 ...

  6. 2020年Java程序员不得不学习的10大技术

    对于Java开发人员来说,最近几年的时间中,Java生态诞生了很多东西.每6个月更新一次Java版本,以及发布很多流行的框架,如Spring 5.Spring Security 5和Spring Bo ...

  7. Java程序员三年的工作经验,却不如一个新人的工资高???

    文章目录 一.关于程序员的几个阶段 第一阶段:三年 第二阶段:五年 第三阶段:十年 二.关于项目经验 三.关于专业技能 1.基本语法 2.集合 3.设计模式 4.多线程 5.JDK源码 6.框架 7. ...

  8. java程序员遇到的问题_Java 程序员平时最常遇到的故障:系统OOM (一)

    作为 Java 程序员而言,先不考虑自己系统外部依赖的缓存.消息队列.数据库等等东西挂掉,就我们自己系统本身而言,最常见的挂掉的原因是什么? 其实就是系统OOM,也就是所谓的内存溢出! 什么是内存溢出 ...

  9. java程序员入门先学什么开发者工具

    学习java编程语言,那么开发工具是肯定少不了的,程序员入门基础中开发工具是一定要学会的,可以帮助开发者们提高开发效率.更优雅的写代码.由于开发者涉及的技术领域众多,以后端开发者的视角盘点平时可能用得 ...

  10. 优秀的Java程序员应具备哪些编程技术?

    想要成为一名合格的java程序猿,需要学习的知识是有很多的,但是基础知识一定要非常牢固,基础不牢固的程序员,随时都会被新的知识和技术所淘汰,下盘不稳风一吹就倒,那么具体作为一个优秀的Java程序员应具 ...

最新文章

  1. get传递中文产生乱码的解决方式汇总
  2. 虚拟服务器目录,服务器虚拟主机目录
  3. leetcode算法题--反转单词前缀
  4. Sed教程(五):管理模式、正则表达式、使用功能
  5. 【C++】Visual studio样式定制
  6. caffe 下测试 MNIST数据
  7. 内存spd规范_C语言内存泄露很严重,如何应对?
  8. spring 容器技术入门
  9. 嵌入式Linux入门:概述
  10. 《算法分析与设计(第5版)》——王晓东 - 学习记录 / 期末复习
  11. matlab 图像分割
  12. 简易vbs脚本实现在浏览器自动刷新网页。
  13. python打开qq并登录_python爬虫入门之qq登陆初探
  14. 斯坦福大学C语言课程观后感,看《斯坦福大学公开课:编程方法学》有感之一...
  15. 计算机图形图像处理专业学什么,计算机图形图像处理教学大纲
  16. python中常数e_常数python_python 常数_python 常数e - 云+社区 - 腾讯云
  17. anbox 使用情况_Anbox让您在Linux桌面上运行Android应用程序
  18. Python用最简单的代码画出一箭穿心
  19. FPGA——PS/2驱动
  20. 破解滑块(极验)验证码思路分享

热门文章

  1. 一款APP的完整开发流程
  2. 川蔚蓝:追求品质、创新发展;以诚为根,以客为本
  3. 2015最新移动App设计尺寸视觉规范【图文版】(转)
  4. 罗永浩当年求职新东方一万多字的求职信
  5. 结婚率下滑,离婚率攀升,用BI做分析后,结论扎心了
  6. 关于APP 内涉及用户个人敏感信息/权限的进一步整改
  7. android 华为 imei,华为手机EMUI系统查看S/N IMEI/MEID 产品识别码的方法
  8. 从教女儿下棋谈启蒙教育
  9. Linux 配置Host
  10. 中柏平板bios对照表_BIOS详解及中英文对照表