在中国,对产品之间的互相借鉴有着病态的宽容,也有着病态的苛刻。
谈及这个话题,首先得对“产品抄袭”下一个定义。
问:仿效人家产品独创性,标志性的细节算不算抄袭?
答:算,怎么不算。
哎哟,糟了。在APP上成为标配的“下拉刷新手势”,源自于Twitter客户端Tweetie的独创性设计,我记得还申请了专利。但你看现在满大街的APP都下拉刷新,甚至苹果自己的iOS6里边也应用了这一手势……
今年10月,Mac版Tweetie停止服务。科技媒体对它的悼文里写道:Mac版Tweetie 2009年4月20日推出,引入了自己的一套UI范式,迅速在整个Mac应用设计圈中蔓延开来。例如侧边栏导航几乎被用于所有Twitter客户端;导航栏上展示当前位置的“小突起”也是布里克特的发明,Google+、Instapaper等数百款应用借鉴了这一元素。
这么多抄袭者,不以为耻反以为荣,唉,唉。
问:细节就算了,仿效人家产品独创性,标志性的核心模块算不算抄袭?
答:算,怎么不算。
哎哟,糟了。你说这源自于Pinterest的瀑布流……现在满大街都玩瀑布流,生怕成为时代的落伍者。难道第1个抄袭者被千夫所指,第100个抄袭者就是紧跟潮流?
这逻辑不通。
即便谈道德,也须讲逻辑。即便是痛恨抄袭的人,也需要直面那些你设计的产品(或你喜爱的产品),其中大量的细节与模块来自于“仿效人家产品”。独创性的设计被抄得多了,便模糊了出处,变成时髦甚至是惯例,并且在复制中得到花样百出的改进。难道因为大家都来抄这个就不算抄了吗?亲爱的,请回答我。
问:好吧,不谈局部抄袭,全局性地仿效人家产品,整个照搬过来算不算抄袭?
答:算,怎么不算。
哎哟,糟了。当年照搬ICQ的OICQ……现在还有人说QQ山寨了ICQ吗?恰恰相反,QQ被视为中国最杰出,最创新的产品之一,而ICQ早已泯然众人矣。
曾经照搬Google的百度,后来出了贴吧这样杰出而创新的产品。曾经照搬Twitter的新浪微博,也有很多创新,甚至被Twitter回过身来仿效借鉴。这时,亲爱的,你是否恨铁不成钢,心想怎么就不能从头创新到尾,一开始非得山寨人家,污了名节?
有此志气者,宜从自己做起。
回到正题,说说我的产品抄袭观。我对于仿效人家的优秀设计,从来都没有任何羞耻感,有好的就拿过来用呗。看见带感的新奇设计常呵呵直乐,大喊道:抄!拿过来抄!
但是,如果我的产品里没有独创性的,值得夸耀,甚至值得让人家来抄的设计,我会感到强烈的羞耻。
很多人拿苹果胜诉三星的案子来说事,请先搞清楚,三星抄的是工业设计。互联网产品设计的专利诉讼有经典案例吗?我没有印象。譬如以创新而闻名的 Facebook曾经仿效FourSquare成名的CheckIn功能,结果做砸了;最近又把仿效Twitter的“Subscribe”功能改名为 “Follow”,展现出抄到底的决意。奇怪的是腾讯这么干必定被口水淹死,大洋彼岸的领军人物却受教众膜拜……
即便谈道德,也须讲逻辑。
在中国,对产品之间的互相借鉴有着病态的宽容,也有着病态的苛刻。细节不能仿效,模块不能仿效,全盘更不能仿效。只要产品与他人有些相似,耻笑声便滚滚而来:“山寨货”“抄那个谁谁谁的”“中国人怎么就知道抄?”与之对应的,又有另一种大嗓门轰隆隆作响:“谁他妈不抄啊”“活下来最重要”“赚到钱才是大爷!”
话不投机,说点别的。
我虽然不介意抄,甚至乐于去抄,但经常苦恼于找不到抄的对象。我做产品,首先在心里边有一个很明确的意象,知道自己想做出什么样子,再围绕着它来设计架构,填充细节,从若干款产品里分别提取我想要的部分来混搭。但如果意象和架构比较特别,经常连看数十款产品还苦着脸说“没得抄啊”,觉得都卯不上构思。被逼得没法子时只好凭空去画原型,自嘲为“狗急跳墙的创新”。
比如做手头上这款APP的时候,同事常听见我喃喃自语“咋个又没得抄喃……”
有时候又喜洋洋地指着原型说“这里,这里,都是世界各国均无先例的设计哦!”
所以有人说我仅仅是山寨erly而已,淡淡一笑。也有人说我的创新设计很赞,也淡淡一笑。还有竞品把我们的一部分创新设计1比1仿制,在蝉小队激起欢笑的声浪。
在我看来,抄并不羞耻;瞎抄,生搬硬套,抄回来消化不良上吐下泻,这才是羞耻中的战斗机。毕竟每款产品都有自己的情景逻辑,整体构思,市场背景,搞不懂这些因缘,一看人家的设计cool就动手抄,很容易东施效颦。而有能力抄到位的团队,基本上也有能力在仿制过程中作出差异化的设计,注入自己的风格与构思,相当于创意的分裂繁殖。
另外,对于国内产品仿效国外产品,鄙行业的反应也忒大了点。大部分国外产品明摆着不重视中国市场,难道让国内用户翻墙去用英文原版?天方夜谭嘛。有些人看盗版电影美剧的时候理直气又壮,觉得国内不引进,难道让我海外代购英文原版?同时又指着国外产品的仿制者怒叱:可耻!人家明明是翻墙可用的!
作为一个用户(而非从业者),我希望用到好的产品。一边是国外产品全英文界面速度又慢,一边是仿制品全中文本地化秒开网页,你叫我怎么选?我也想支持原版,先挂上VPN再恶补英文三级好咩?那些不会翻墙英文不好的用户就注定“不在服务区”好咩?
显而易见的是,国外的好产品大多数不进来,更不会为中国市场进行本地化调整,如果没有人去仿制,移植,国内用户就得不到好的互联网服务。大喊“为什么不创新”固然豪情盖天,有此志气者,宜从自己做起。何况仿制移植只是个开始,在本地化的过程中,产品会渐渐生长为另一个样子——毕竟用户群大相迥异。基因的复制与分裂不断进行,才能构成繁荣的多样化互联网生态。
我从不介意谁抄了谁谁谁,但我会介意他有没有抄到位,有没有成熟的移植与聪明的本地化。如果在仿制之外更添出彩之处,那么,他还会赢得我的尊敬。
最后,举一个粗浅的例子作为尾声。APP语音对讲大约是TalkBox首创,其后成为IM标配。前些日子我偶然安装了TalkBox,大失所望,各方面都不如后来者。想起一年前千夫所指微信山寨TalkBox,亲爱的,你更愿意追捧一款整体品质平平的原创应用,又或者语音IM在基因复制并分裂后,遍地开花争奇斗艳?
这个案例里,复制不仅没有扼杀创新,借语音之力青云直上的微信,反倒做出了更多的创新动作;而始作俑者TalkBox后来波澜不惊,也与其“一招鲜”有关,后续版本缺乏打动市场的亮点,不能单怪微信只手遮天。
数十亿美元就这样打了水漂——今年多个软件项目遭遇失利,此类事故已经引起管理者的高度重视。
诚然,很多企业在软件项目的推进过程中获得了成功,也将供应商所承诺的新特性与新功能顺利传递给终端用户:更低的运营成本、更简洁的管理流程以及各类足以取悦消费者的要素。
但遗憾的是,仍然有一些项目以失败告终;投入巨资的客户只能面对“断瓦残垣”而欲哭无泪,并被卷入危害事业进展、有损合作关系的漫长诉讼当中。
而从积极的角度来看,我们能够将这些过往的事故当作前车之鉴,无论是供应商还是项目客户都能够从中吸取经验教训。下面就请大家一同关注2012年最可怕的软件项目事故汇总。
耗资十亿美元,美国空军一怒之下叫停ERP项目
今年十一月,有报告称美国空军决定放弃名为“远程作战保障系统”(简称ECSS)的主要ERP(企业资源规划)软件项目,因为空军管理者发现在投入高达十亿美元的建设资金后,该项目并未带来“任何显著的军事能力提升”。
早在2005年就已诞生的ECSS项目计划更换超过两百套遗留系统,但这套以甲骨文软件为基础的方案在成本方面发生了无法扼制的恶性膨胀。美国空军官员与系统承包商CSC引入了大量计划之外的附加定制编码工程与集成任务,这一步步将项目推向了灭亡的边缘。
一位空军军方发言人指出,该项目需要再投入十一亿美元才能完成既定目标的四分之一,而且即使一切顺利,项目也无法在2020年之前整体竣工。
监督机构的调查报告显示,美国军方所着力打造的军事ERP项目遭到彻头彻尾的失败
美国政府问责办公室(简称GAO)于今年三月发布的报告宣称,许多由军方发起的ERP项目不仅在进度上落后于计划,更是严重超出财政预算。
其中典型的例子要数海军陆战队搞出来的“全球作战支持系统”,其实际耗资几乎为初期预算的十倍,本该于2009年11月就正式完成。而根据问责办公室的统计,其最终开销达到十一亿美元,远超过当初定下的1.26亿美元。
海军发起的另一个ERP项目始于2003年,计划于2011财年之内完成。然而由于各种原因,项目最终将于2013年8月竣工,实际开发成本也由最初的十九亿美元增长到二十七亿美元。
“这样的情况令我们难以接受,也许政府应该把那帮一拍脑门也做决策、把几十亿美元投入垃圾项目的家伙们踢出门去,”咨询企业Asuret公司CEO兼分析师Michael Krigsman在采访中表示,他同时也是一位杰出的IT项目管理专家。
“最大的问题在于,这类失败是怎么发生的?”Krigsman补充道。“控制机制何在?为什么这样的状况屡屡出现,却没人站出来加以阻止?政府的IT部门是不是已经掌握不了局势了?”
首先介绍两个方法 SEL和@selector
根据AppleObjective-C Runtime Reference官方文档这个传递消息的函数就是 id objc_msgSend(id theReceiver, SEL theSelector, …)
theReceiver是接受消息的对象类型是id,theSelector是消息名称类型是SEL。下边代码我们来看看如何来生成一个SEL,如果传递消息。
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
然后调用它
[self performSelector:@selector(fooNoInputs)];
第二个试验看看如何在消息中传递参数
我们建立一个有input参数的函数
- (void) fooOneIput:(NSString*) first {
NSLog(@"Logs %@", first);
}
然后调用它
[self performSelector:@selector(fooOneInput:) withObject:@"first"];
第三个试验更多的参数
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
}
然后调用它
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first"withObject:@"second"];
第四个试验如何建立动态的函数,然后调用他们?我们需要建立一个selector
SEL myTestSelector = @selector(myTest:);
并且我们调用的函数在另外一个Class内
- (void)abcWithAAA: (NSNumber *)number {
int primaryKey = [number intValue];
NSLog("%i", primaryKey);
}
MethodForSelectors * mfs = [[MethodForSelectors alloc]init];
NSArray *Arrays = [NSArray arrayWithObjects:@"AAA", @"BBB", nil];
for ( NSString *array in Arrays ){
//SEL customSelector=@selector(@"`.......");
SEL customSelector = NSSelectorFromString([NSStringstringWithFormat:@"abcWith%@:", array]);
mfs = [[MethodForSelectors alloc] performSelector:customSelector withObject:0];
}
1.如果使用了ARC会产生“performSelector may cause a leak because its selector is unknown”警告
2.这种方式当传入一个不符合约定的消息时会继续运行并不报错。例如应该传入2个参数,但只传入1个参数。或传入了3个参数,第三个参数不会被初始化。
还有一种调用其他Class Function的方法是,但是不能有参数,我们这里假设没有参数,那么就可以这样
[mfs customSelector];
完整的代码:
@implementation ClassForSelectors
- (void) fooNoInputs {
NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
NSLog(@"Logs %@ then %@", first, second);
}
- (NSArray *)abcWithAAA: (NSNumber *)number {
int primaryKey = [number intValue];
NSLog("%i", primaryKey);
}
- (void) performMethodsViaSelectors {
[self performSelector:@selector(fooNoInputs)];
[self performSelector:@selector(fooOneInput:) withObject:@"first"];
[self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first"withObject:@"second"];
}
- (void) performDynamicMethodsViaSelectors {
MethodForSelectors * mfs = [MethodForSelectors alloc];
NSArray *Arrays = [NSArray arrayWithObjects:@"AAA", @"BBB", nil];
for ( NSString *array in Arrays ){
SEL customSelector = NSSelectorFromString([NSStringstringWithFormat:@"abcWith%@:", array]);
mfs = [[MethodForSelectors alloc] performSelector:customSelector withObject:0];
}
}
@end
- (void)abcWithAAA: (NSNumber *)number {
NSLog("%i", number);
}
@end