优大网

月份存档: 3 月 2013 ( 2 / 2)

五月天 – 温柔

作词:阿信 作曲:阿信

走在风中 今天阳光 突然好温柔

天的温柔 地的温柔 像你抱着我
然后发现 你的改变 孤单的今后
如果冷 该怎么度过

天边风光 身边的我 都不在你眼中
你的眼中 藏着什么 我从来都不懂
没有关系 你的世界 就让你拥有
不打扰 是我的温柔

不知道 不明了 不想要 为什么 我的心
明明是想靠近 却孤单到黎明
不知道 不明了 不想要 为什么 我的心
那爱情的绮丽 总是在孤单里
再把我的最好的爱给你

不知不觉 不情不愿 又到巷子口
我没有哭 也没有笑 因为这是梦
没有预兆 没有理由 你真的有说过
如果有 就让你自由

不知道 不明了 不想要 为什么 我的心
明明是想靠近 却孤单到黎明
不知道 不明了 不想要 为什么 我的心
那爱情的绮丽 总是在孤单里
再把我的最好的爱给你

不知不觉 不情不愿 又到巷子口
我没有哭 也没有笑 因为这是梦
没有预兆 没有理由 你真的有说过
如果有 就让你自由 自由

这是我的温柔
这是我的温柔
这是我的温柔
这是我的 温柔

你最大的敌人是自己:一个独立游戏人的创业自白

摘自http://tech2ipo.com/56765

你最大的敌人是自己:一个独立游戏人的创业自白

本文作者是一名独立游戏开发者(weibo),他开发的游戏在最近已经升至App Store付费榜第三,祝福他的独立游戏之路顺利。

你最大的敌人是为什么自己?原来我也无法理解,但后来太多的事情都证明了这一点。比如说:我工作中会经常和陌生人打交道,而我又是一个内向的人,所以经常都是没有开口之前就咽回去了,因为自己害怕拒绝。后来自己突破了自己的想法,现在和陌生人交流都是如鱼得水,很少遇到拒绝。难道是别人变了吗?不是,是自己战胜了自己。当你自己的内心改变了,整个世界都变了。你自己悲观了,看整个世界都是灰色的,你自己积极了,整个世界都充满了欢笑。就这么简单。所以说,很多时候我们不是无法战胜困难了,而是无法战胜自己的内心了,是我们的心过不去了。

我最近一直体验着自己是自己的最大敌人。我做 iOS 独立游戏人已经快两年了。大家都以为苹果的应用能发大财,于是我也头脑一热“跳进”了苹果开发者大军中,做起了独立 App 开发者,现在则成了一名不折不扣的独立游戏人。

刚开始的头一年就赔的快底朝天了,压力超级大,还时不时的向很多年都不经常联系的朋友借钱。那种心境太难熬了,你要拉下脸面到处去找熟悉的朋友借钱,面见投资人时,一次次被拒绝,于是就静下心来反思:做 iOS 游戏是我的第二次创业,也是我创业10多年后真正找到了自己的事业归宿:兴趣+创业的完美结合。做了10年的“乙方”,这次一定要做好自己的“甲方”,自己掌握自己的命运:一定活在自己的世界里!一定要给自己有一个“完美的故事”。

由于自己从小就喜欢画画,从中央美院结业后,1998年就开始了我人生的第一次创业生涯,第一次创业自己开了一个品牌创意设计公司,一干就是10年。这十年收获颇大,也经历了很多辛酸苦辣:某天你去银行时,你的银行户头分文没有了,被财务卷跑了。业务被业务员”短路“了。睡了好几年的办公室地面,加班加到第二天呼吸时都是血腥味。本来是很好的创意方案,硬是被无知的甲方给否了……

10年历练了我的心智:坚持!

做 iOS 应用1年后,经验还是不足,还是要交学费,但绝对不能浮躁。自己的应用下载量还没上来,没有“干货”,我给自己的决心是,做好自己的“干货”,让钱来找你,就是不融资也能活下来。我看到了一些类似的个人创业者跟我刚开始一样:听了别人说XXX融了几千万美金等等,出个概念就去融资,天天做着不靠谱的发财梦,挺不了多长时间,项目就黄了。

不能为了融资而融资,于是就静下心来潜心研究苹果应用的开发方向、营销方营销渠道。有时压力大了就看看夜空的星星,“看的远一点,一定要战胜自己”,想想我最喜欢的游戏开发行业,看到我们自己的作品,还是顶着压力过来了,“创业首先要打败自己”。  半年前,在创意苹果休闲游戏的时候,我就在反思ios游戏的开发方向问题。

休闲游戏从娱乐的角度来讲,带给玩家最大的意义是什么?能不能玩游戏打发碎片时间的同时,还能带给用户一些关于生活的功用性启示?于是就下定决心运营了一个益智类的休闲游戏品牌 dodosmart。经过3个月的开发,我的处女作:贪玩的豌豆(PlayfulPeas) 跳跃休闲游戏终于上线了,这款游戏帮用户所解决的问题就是:游戏主角豌豆不断通过跳跃去挑战自我的高度极限。其实豌豆也像现实中的你一样,人生最大的敌人其实不是别人,而是你自己;你要不断的挑战自己,挖据自己的潜能极限、用积极的心态面对困难、超越困难、超越自我,给自己不断树立新的目标,用阳光的心态实现你的一个又一个的人生目标……

突破自己,每人都有许多美好的梦想,每人都希望寻到自己的最佳位置,实现自己的最大价值,有时会在瞬间被无情地现实打击的粉碎。在很多时候,人人都需要在心中添把火,以重新燃起某些希望。人在一生中会遇到成千上万个对手,如每一次赛跑、每一次竞争、每一个利益……数也数不清。人生最大的敌人、最难对付的敌人是谁呢?其答案就是自己是自己的最大敌人。这也是“人生十四最”中的第一句话。

我琢磨了许久,人要想超越他人,要想成功,就必须先超越自己,战胜自己的意识、信心和外界一切压力。而当面对挫折和困难时,却往往容易被这些意识、信心、压力打败,从功亏一篑,败给自己。人的一生其实就是自己与自己斗争的过程。当你为争权夺利而辗转反侧、为一已之利而耿耿于怀、为年轻气盛而目空一切、为别人的恭维而飘乎迷失、为一时的失利而嗟叹悔恨、为失去的情感而垂头丧气的时候,确实需要拿出很大的勇气来战胜自己,你要使出浑身懈数,不停地安慰自己、开导自己、压抑自己、调整自己,当你被你的负面情绪完全俘虏时,你也就坠入了痛苦的深渊。这时是很需要自己来拉自己一把的,因为只有自己才是自己的救世主,也只有自己才能拯救自己。

战胜自己,说起来容易做起来却很难,你无法目眼睁睁地看着业务不如你的同行比你先行提拔却心理平衡;你不可能看着上学时的同学或哥们开着香车宝马和你炫耀而无动于衷………这世界有太多的诱惑、太多的陷井、太多的玄机,让人琢磨不透、欲罢不能,让人深陷其中,不能自拔。这时候真得需要很大的勇气让自己摆脱这一切,让自己的心冷静到平淡。只有当真正沉静下来的时候,你才会发现生活原来可以非常简单。

让心变得简单、让心无欲而刚,当你以俯览的心态看待这一切时候,才发现:芸芸众生、世态百相、善恶美丑、利益纷争不过是一场不停上演的舞台秀,我们每个人都不过是其中的一个小角色,你方唱罢我登场,好不热闹,我们沉缅其中随波逐流,而从来没有想到你拼了命得到的东西有时并不是你真正需要的,并没有想到人生也要剧终、戏剧终将落幕,而最终我们连一片云彩也不能带走。以旁观者的心态来看自己,来看这一切,就会觉得自己原来是这么滑稽,象一个失去灵魂的木偶,在世俗的流里漂浮出没,渐渐迷失了自己。

用感恩的心态看待生活,才发现生活给予我们的已太多太多。感谢朋友,他让你不再寂寞;感谢工作,它使你衣食无忧;感谢知识,它使你不再无知;感谢挫折,它使你变得坚强;感谢生命,它让你感知快乐;感谢父母,他们赐予你生命。你也许并不美丽,但你至少有气质;你也许没有气质,但你拥有温柔;你也许并不温柔,但你至少善良,你也许并不善良,但你拥有健康的身体。

每人都有闪光点,每人都有很多优点,世上缺乏的不是美,而是缺乏发现美的眼睛,看一棵树,看花,看夜,看枝干,只要你带着欣赏的眼睛,带着审美的心情,总能在一棵树上发现美来。人生是什么?人生就是挑战,一个消极的人,只会在机会里看到难题,一个积极的人,能从难题里看到机会。假如困难和挫折像洪水一样涌来,将你无情的甩进痛苦的深渊,不要呻吟,不要悲伤,而要咬紧牙关,挺起胸膛,勇敢地向逆境挑战,失落的太阳就会重新升起。面对无情的打击和阴冷的舆论所构成的压力,不要逃避也不要妥协,只要你坚信是对的事,就让别人去说吧。坚定地走自己的路,便冲出重围,开创辉煌的事业。

人生最大的敌人的自己、人生最大的失败是自大、人生最大的无智是欺骗、人生最大的悲哀是嫉妒、人生最大的错误是自弃、人生最可配的是精进、人生最大的破产是绝望、人生最大的财富是健康、人生最大的债务是情债、人生最大的礼物是宽怒、人生最大的缺欠是悲智、人生最大的欣慰是布施、人生最可怜的性情是自卑、人生的罪过是自欺欺人。

每人总会遇到一些小人、伪君子等可恶之徒,但是都是对你心智上的历练,其实有些时候是谁成就了你?你遇到的小人让你看清了世间的丑陋,让你时刻警醒自己;你遇到的敌人让你更有斗志,让你成为强人,圣人说得好:生于忧患,死于安乐。

我们毕竟是“凡夫俗子”,我们也要善待竞争,培养竞争意识和竞争能力,生活不可能时时处处都适应你,而你却要学会时时处处适应生活,才能永远立于不败之地。一个人要期待命运的恩赐,不要求一帆风顺的航程,只有做一个勇敢的挑战者,才能获得美好而又富有的人生。但是,你只要静下心来想想,你只要把自己放到一个更高的位置,就像是站在山顶看山下、站在火星看地球、站在月亮观四海一样,你会发现有更多的前所未见的很多东西,你就能超越自我,也包括超越自我的眼光和局限。走出自我,真正地行动起来,用美德感悟这个世界的神奇,用爱心去体会这个时代的绚丽,用品格体验现实的真谛,用精神渲染人际的和睦,你就会发现战胜自己并不是一件难事。

只要人有了信心,就会产生意志力量。人与人之间,弱者与强者之间,成功与失败之间最大的差异就在于意志力量的差异。人一旦有意志力量,就能战胜自身的弱点:

当你需要勇气的时候,就能战胜自己的懦弱;

当你需要勤奋的时候,就能战胜自己的懒惰;

当你需要谦虚的时候,就能战胜自己的骄傲;

当你需要宁静的时候,就能战胜自己的浮躁。

“自己把自己说服了,是一种理智的胜利;自己把自己感动了,是一种心灵的升华;自己把自己征服了,是一种人生的成熟。”

一个人活着怎样活的快活充实,并不是你别人强,比别人行就能满足。你克服了自己的问题,体验到了进步,感受到的那份成功,才会让你有成就感,才是你人生价值的升级,就像豌豆的跳跃世界一样,进入他的世界,也是进入你的世界,看看你要即将发生的传奇故事!

如何编写出拥抱变化的代码?

http://www.csdn.net/article/2013-02-25/2814251-coding-change

发表于2013-02-26 09:06| 13321次阅读| 来源net.tutsplus| 70 条评论| 作者Patkos Csaba

摘要:编写高效优质的代码一直是程序员所追求的目标之一,那么什么样的代码才叫优质呢?其中最重要的莫过于易维护、易修改。本文作者从面向对象和SOLID两大方面,非常详细地总结了如何编写出易修改的代码,绝对让你受益匪浅。

在实际的开发中,编写出易维护和易接受变化的代码并非易事,想要实现可能更加困难重重:源码难于理解、依赖关系指向不明、耦合也很令人头疼。难道就真的就没有办法了吗?本文中我们一起探讨几个技术原则和一些编码理念,让你的代码跟着需求走,而且易维护易拓展。

介绍些面向对象方法

面向对象编程(OOP)是一种很受欢迎的编程思想,它保证了代码的组织性和重用性。软件公司采用OOP思想编程已经好多年了,如今仍然在项目开发中使用这一思想。OOP拥有一系列非常好的编程原则,如果使用恰当,它会让你的代码更好、更整洁和更易维护。

1.内聚力

这里的内聚力是指拥有一些共同的特征的东西而逐渐凝聚到一起,而不能在一起的东西则会被移除出去。可以用一个类来说明内聚力:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ANOTCohesiveClass {
   private $firstNumber;
   private $secondNumber;
   private $length;
   private $width;
   function __construct($firstNumber$secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function setLength($length) {
      $this->length = $length;
   }
   function setHeight($height) {
      $this->width = $height;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
   function area() {
      return $this->length * $this->width;
   }
}

 

该例定义了一个类以及一些表示数字和大小的字段。而这些属性通过他们的名称来判断是否应该在一起。add()和substract()方法来对两个number进行操作,此外还定义了area()来操作length和width这两个字段。

这个类只负责各个独立的群体信息,显然,内聚力很低。重构上面的例子:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ACohesiveClass {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber$secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}

重构以后,该类明显变成了高内聚特征的类。为什么?因为这个类里的每个部分都与另外一部分彼此联系。虽然在实际开发中编写出高内聚的类比较困难,但开发人员应该坚持这样做,坚持就是胜利。
2.正交性

就简单而言,正交是指隔离或排除副作用。一个方法、类或者模块改变了其他无关的方法、类或模块就不是正交。例如,飞机的黑匣子就具有正交性,它自身就具备电源、麦克风和传感器等这些功能。而它对外在的其他东西没有任何影响,它只提供一种机制,用来保存和检索飞行数据。

一个典型的非正交系统例子就是汽车电子设备。提高汽车的速度也存在些负面影响,比如会增加无线电音量,然而对汽车来说,速度并不是正交。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Calculator {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber$secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      $sum $this->firstNumber + $this->secondNumber;
      if ($sum > 100) {
         (new AlertMechanism())->tooBigNumber($sum);
      }
      return $sum;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class AlertMechanism {
   function tooBigNumber($number) {
      echo $number 'is too big!';
   }
}

在这个例子中,Calculator类里的add()方法里列了几个意想不到的行为:它生成AlertMechanism对象并调用其中的一个方法。实际上,该库的使用者并不希望消息被打印到屏幕上,相反,他们则是要计算数字之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Calculator {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber$secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class AlertMechanism {
   function checkLimits($firstNumber$secondNumber) {
      $sum = (new Calculator($firstNumber$secondNumber))->add();
      if ($sum > 100) {
         $this->tooBigNumber($sum);
      }
   }
   function tooBigNumber($number) {
      echo $number 'is too big!';
   }
}

这样明显好多了,AlertMechanish在Calculator中没有任何负面影响,相反,在任何需要弹出警告的地方都可以使用AlertMechanish。

3.依赖和耦合

大多数情况下,这两个单词是可以互换的,但是在某些情况下,又存在优先级关系。

那么,什么是依赖呢?当对象A需要使用对象B时,为了执行其规定的行为,我们说A依赖B。在OOP中,依赖是极其常见的。对象之间经常互相依赖才发挥功效。因此消除依赖是一项崇高的追求,这样做几乎是不可能的。控制依赖和减少依赖则是非常完美的。

就紧耦合(heavy-coupling)和松耦合(loose-coupling)而言,通常是指一个对象依赖于其他对象的程度。

在一个松耦合系统中,一个对象的变化会减少对其依赖对象的影响。在这样的系统中,类取决于接口而不是具体的实现(将会在下面提到)。这就是为什么松耦合系统对修改更加开放的原因。

Coupling in a Field

让我们看下面这个例子:

 

1
2
3
4
5
6
class Display {
   private $calculator;
   function __construct() {
      $this->calculator = new Calculator(1,2);
   }
}

 

这段代码很常见,在该例中,Display类依赖Calculator类并直接引用该类。Display类里的 $calculator字段属于Calculator类型。该对象和字段直接调用Calculator的构造函数。

 通过访问其他类方法进行耦合

大家可以先看下面的代码:

 

1
2
3
4
5
6
7
8
9
class Display {
   private $calculator;
   function __construct() {
      $this->calculator = new Calculator(1, 2);
   }
   function printSum() {
      echo $this->calculator->add();
   }
}

Display类调用Calculator对象的add()方法。这是另外一种耦合方式,一个类访问另外一个类的方法。

通过方法引用进行耦合

你也可以通过方法引用进行耦合:

 

1
2
3
4
5
6
7
8
9
10
11
12
class Display {
   private $calculator;
   function __construct() {
      $this->calculator = $this->makeCalculator();
   }
   function printSum() {
      echo $this->calculator->add();
   }
   function makeCalculator() {
      return new Calculator(1, 2);
   }
}

需引起注意的是,makeCalculator()方法返回一个Calculator对象,这也是一种依赖。

利用多态进行耦合

遗传可能是依赖里的最强表现形式。

 

1
2
3
4
5
class AdvancedCalculator extends Calculator {
   function sinus($value) {
      return sin($value);
   }
}

通过依赖注入降低耦合

开发人员可以通过依赖注入来降低耦合度,例如:

1
2
3
4
5
6
7
class Display {
   private $calculator;
   function __construct(Calculator $calculator = null) {
      $this->calculator = $calculator ? : $this->makeCalculator();
   }
// ... //
}

利用Display的构造函数对Calculator对象进行注入,从而减少了Display对Calculator类产生的依赖。

利用接口降低耦合

例如:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface CanCompute {
   function add();
   function subtract();
}
class Calculator implements CanCompute {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber$secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class Display {
   private $calculator;
   function __construct(CanCompute $calculator = null) {
      $this->calculator = $calculator ? : $this->makeCalculator();
   }
   function printSum() {
      echo $this->calculator->add();
   }
   function makeCalculator() {
      return new Calculator(1, 2);
   }
}

 

该代码定义了一个CanCompute接口,在OOP中,接口可以看作一个抽象类型,它所定义的成员必须由类或结构来实现。在上述代码中,Calculator类来实现CanCompute接口。

Display构造函数期望有个对象来实现Cancompute接口,这时,Display的依赖对象Calculator被打破。然而,我们可以创建另一个类对象来实现Cancompute,并且传递一个对象到Display的构造函数中。Display现在只依赖于Cancompute接口,但即使这样依赖关系仍然是可选的。如果我们不传递任何参数给Display的构造函数,那么它将通过调用makeCalculator()方法来创建一个Calculator对象。这种技术经常被开发者们使用,尤其对驱动测试开发(TDD)极其有帮助。

SOLID原则

SOLID是一套代码编写守则,也就是大家常常说的敏捷开发原则,最初由Robert C. Martin所提出。使用它编写出来的代码不仅干净整洁,而且易维护、易修改和易扩展。实践表明,其在可维护性上有着非常积极的影响,更多资料大家可以阅读: Agile Software Development, Principles, Patterns, and Practices

SOLID所涵盖的话题非常广,下面我将会针对本文的主旨介绍一些简单易学的方法。

1.单一责任原则(SRP)

一个类只干一件事。听起来简单,但在实践中却可能相当难。

 

1
2
3
4
5
6
class Reporter {
   function generateIncomeReports();
   function generatePaymentsReports();
   function computeBalance();
   function printReport();
}

 

查看上面的代码,你认为该类的受益者会是哪个部门?会计部是用于收支平衡、财政部可能用来编写收入/支出报告,甚至归档部来打印和存档报告。然而每个部门都希望有属于自己的方法,并且根据自身需求来做些自定义的方法。

这样的类往往都是高内聚低耦合的。

2.Open-Closed原则(OCP)

类(和模块)应具备很好的功能扩展性,以及对现有功能具有一定的保护能力。让我们一起来看下典型的电风扇例子,你有一个开关来控制风扇:

 

1
2
3
4
5
6
7
8
9
10
11
12
class Switch_ {
   private $fan;
   function __construct() {
      $this->fan = new Fan();
   }
   function turnOn() {
      $this->fan->on();
   }
   function turnOff() {
      $this->fan->off();
   }
}

 

这段代码创建了Switch_类,用来创建和控制Fan对象。注意这里的下划线,在PHP中是不允许把类名定义为Switch的。

这时,你的老板希望能利用该开关控制电风扇上的电灯,那么你就不得不修改Switch_这个类。

对现有代码进行修改存在一部分风险,很有可能对系统其他部分产生影响。所以在添加新功能时的最好的方法是避开现有功能。

在OOP中,你可以发现Switch_对Fan类有很强的依赖性。这正是我们的问题所在,基于此,做出如下修改:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface Switchable {
   function on();
   function off();
}
class Fan implements Switchable {
   public function on() {
      // code to start the fan
   }
   public function off() {
      // code to stop the fan
   }
}
class Switch_ {
   private $switchable;
   function __construct(Switchable $switchable) {
      $this->switchable = $switchable;
   }
   function turnOn() {
      $this->switchable->on();
   }
   function turnOff() {
      $this->switchable->off();
   }
}

 

该代码定义了一个Switchable接口,它里面所定义的方法需要开关启用选项来实现。Fan对象实现Switchable和Switch_并且接受一个参数到Switchable对象的构造函数里。

这样做有哪些好处?

首先,该解决方案打破了Switch_和Fan之间的依赖关系。Switch_不知道它要开启风扇,并且也不关心。其次引进的Light类不会影响Switch_或Switchable。难道你想用Switch_类来控制Light对象吗?代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Light implements Switchable {
   public function on() {
      // code to turn ligh on
   }
   public function off() {
      // code to turn light off
   }
}
class SomeWhereInYourCode {
   function controlLight() {
      $light new Light();
      $switch new Switch_($light);
      $switch->turnOn();
      $switch->turnOff();
   }
}

 

3.Liskov替换原则(LSP)

LSP是指子类永不打破父类的功能,这点是非常重要的。用户定义一个子类只是希望能实现其自有功能,而不是去影响原来的功能。

乍看有点困惑,还是让我们一起来看看代码吧:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rectangle {
   private $width;
   private $height;
   function setWidth($width) {
      $this->width = $width;
   }
   function setHeigth($heigth) {
      $this->height = $heigth;
   }
   function area() {
      return $this->width * $this->height;
   }
}

 

定义一个简单的Rectangle类,我们可以设置它的高度和宽度,并且area()方法可以计算出该矩形的面积。再看下面例子:

 

1
2
3
4
5
6
7
class Geometry {
   function rectArea(Rectangle $rectangle) {
      $rectangle->setWidth(10);
      $rectangle->setHeigth(5);
      return $rectangle->area();
   }
}

 

rectArea()方法接受一个Rectangle对象作为一个参数,设置其高度和宽度并且返回该图形的面积。

正方形乃是矩形中的一个特殊图形,我们定义Square类来继承Rectangle:

 

1
2
3
class Square extends Rectangle {
   // What code to write here?
}

 

我们有好几种方法来重写area()方法并且返回该正方形的宽度:

 

1
2
3
4
5
6
7
8
9
10
class Rectangle {
   protected $width;
   protected $height;
   // ... //
}
class Square extends Rectangle {
   function area() {
      return $this->width ^ 2;
   }
}

 

把Rectangle的字段改为protected,好让Square有访问的权限。从几何的角度来看是非常合理的,因为正方形的边长是相等的,所以返回正方形的宽度是非常合理的。

然而从编程的角度来看又存在一个问题;如果Square是一个Rectangle,把它馈入到Geometry类是没有任何问题的,但这样做以后,Geometry的代码就显的多余,毫无意义可言。它设置了高度和宽度两个值,这也就是为什么square不是rectangle编程。LSP正很好是说明了这一点。

4.接口隔离原则(ISP)

该原则主要集中用在把大接口分成多个小接口和特殊的接口。基本思路是在同一个类中,不同的用户不应该知道不同的接口——除非该用户需要用到那个接口。即使一个用户不需要使用该类的所有方法,但它仍然依赖于这些方法。所以为什么不根据用户需要定义相应的接口呢?

想象下,如果我们要实现一个股票市场应用,我们要有一个经纪人(Broker)来购买和出售股票,并且报告每天的收益和损失。一个简单的实现方法是定义一个Broker接口,一个NYSEBroker类用来实现Broker和一些用户的接口类:创建交易(TransactionUI)和写报告(DailyReporter)。代码可以类似下面这样:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
interface Broker {
   function buy($symbol$volume);
   function sell($symbol$volume);
   function dailyLoss($date);
   function dailyEarnings($date);
}
class NYSEBroker implements Broker {
   public function buy($symbol$volume) {
      // implementsation goes here
   }
   public function currentBalance() {
      // implementsation goes here
   }
   public function dailyEarnings($date) {
      // implementsation goes here
   }
   public function dailyLoss($date) {
      // implementsation goes here
   }
   public function sell($symbol$volume) {
      // implementsation goes here
   }
}
class TransactionsUI {
   private $broker;
   function __construct(Broker $broker) {
      $this->broker = $broker;
   }
   function buyStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->buy($data['sybmol'], $data['volume']);
   }
   function sellStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->sell($data['sybmol'], $data['volume']);
   }
}
class DailyReporter {
   private $broker;
   function __construct(Broker $broker) {
      $this->broker = $broker;
   }
   function currentBalance() {
      echo 'Current balace for today ' date(time()) . "\n";
      echo 'Earnings: ' $this->broker->dailyEarnings(time()) . "\n";
      echo 'Losses: ' $this->broker->dailyLoss(time()) . "\n";
   }
}

 

虽然这段代码可以正常工作,但它违反了ISP。DailyReporter和TransactionUI都依赖Broker接口。然而,它们只使用接口的一部分。TransactionUI使用buy()和sell()方法,而DailyReporter只用到dailyEarnings()和dailyLoss()方法。

你怀疑Broker没有内聚力,因为它的一些方法没有任何相关性。也许你说的对,但是具体答案还得由Broker说了算;销售和购买可能与当前的盈余有相当大的关系。例如当亏本的时候有可能就不会执行购买操作。

此时,你可能会说Broker违反了SRP,因为有两个类以不同的方式在使用它,可能有两个不同的执行者。好吧,其实它并没有违反SRP。唯一的执行者就是Broker。他会根据当前的形式做出购买/出售操作,其最终的依赖对象是整个系统和业务。

毫无疑问,上述代码肯定是违反了ISP,两个UI类都依赖于整个Broker。这是很常见的问题,改变下观点,代码可以这样修改:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
interface BrokerTransactions {
   function buy($symbol$volume);
   function sell($symbol$volume);
}
interface BrokerStatistics {
   function dailyLoss($date);
   function dailyEarnings($date);
}
class NYSEBroker implements BrokerTransactions, BrokerStatistics {
   public function buy($symbol$volume) {
      // implementsation goes here
   }
   public function currentBalance() {
      // implementsation goes here
   }
   public function dailyEarnings($date) {
      // implementsation goes here
   }
   public function dailyLoss($date) {
      // implementsation goes here
   }
   public function sell($symbol$volume) {
      // implementsation goes here
   }
}
class TransactionsUI {
   private $broker;
   function __construct(BrokerTransactions $broker) {
      $this->broker = $broker;
   }
   function buyStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->buy($data['sybmol'], $data['volume']);
   }
   function sellStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->sell($data['sybmol'], $data['volume']);
   }
}
class DailyReporter {
   private $broker;
   function __construct(BrokerStatistics $broker) {
      $this->broker = $broker;
   }
   function currentBalance() {
      echo 'Current balace for today ' date(time()) . "\n";
      echo 'Earnings: ' $this->broker->dailyEarnings(time()) . "\n";
      echo 'Losses: ' $this->broker->dailyLoss(time()) . "\n";
   }
}

 

修改后的代码明显变的有意义而且尊重了ISP。DailyReporter只依赖BrokerStatistics,它无需关心和知道出售和购买这两个操作。另一方面,TransactionUI只关心购买和出售。NYSEBroker和先前的定义是一样的,实现BrokerTransactions和BrokerStatistics接口。

更复杂的例子你可以前往Rober C.Martin博客上查看 The Interface Segregation Principle里的首篇论文。

5.依赖倒置原则(DIP)

这条原则指出高层模块不应该依赖低层模块,两者都应该依赖于抽象。抽象不应该依赖细节,细节反过来应依赖于抽象。简单地说,你应该尽可能的依赖于抽象而不是实现。

DIP的诀窍是你想反转依赖,但是又想一直保持着整个控制流。回顾下OCP(Switch和Light类),在原始实现中是直接利用开关来控制灯的。

 

你会看到整个依赖和控制流都是由Switch流向Light。当不想直接控制Light时,你可以引进接口这一概念。

 

非常神奇!引进接口后,代码同时满足了DIP和OCP两大原则。正如你上图所看到的,倒置了依赖,但整个控制流是不变的。

高级设计

关于代码的另一重要方面是高级设计和通用体系结构。一个混乱的架构所产生的代码往往是很难修改的,所以保持一个干净整洁的架构是必不可少的,第一步就是理解如何根据不同的内容分离代码。

 

在这张图中,最主要的部分是业务逻辑,它能够如预期那样正常有效的工作并且与其他部分不存在任何瓜葛。站在高级设计角度可以看作为正交性。

从右边的“main”开始看,箭头进入应用程序——创建对象工厂。一个理想的解决方案是从各个特定的工厂中得到相应的对象,但这有点不切实际。不过当有机会这样做的时候还是要使用,并且让它们保持在业务逻辑之外。

再看底部,定义持久层(数据库、文件访问、网络通信)用来保证信息的持久性。业务逻辑层是没有对象知道持久层是如何工作的。

左边则是交互机制。MVC比如Laravel、CakePHP,只能是交付机制而已。

当你看到应用程序架构或目录时,你应该注意其架构是说明程序将要做什么,而不是使用什么技术或数据库。

最后,为了确保所有的依赖项都指向业务逻辑层。用户接口、工厂、数据库则是具体的实现,而你永远不要只依赖于它们。依赖倒置指向业务逻辑模块,无需修改业务逻辑的依赖关系即可允许我们改变依赖。

关于设计模型

在使代码变得易于修改和理解的过程中,设计模型扮演着非常重要的角色。从结构的角度来看,设计模式显然是很有好处的,它们是行之有效并且深思熟虑的解决方案。更多关于设计模式内容,可以前往 Tuts+ Premium course 

测试的力量

测试驱动开发(TDD)所编写出来的代码是很容易测试的。TDD迫使你尊重以上原则来编写代码,从而使你的程序更易被测试。单元测试运行速度很快,应该非常快,当你在一个类里使用10个对象来测试一个单独方法时,你的代码很有可能是有问题的。

总结

俗话说,实践乃是检验真理的唯一标准,所以开发者只有在平时的工作中坚持使用这些原则才能编写出理想的代码。与此同时,不要轻易满足于自己所编写出的代码,要努力让你的代码易于维护、干净并且拥抱变化。(编译/张红月 责编/王然)

来自: net.tutsplus

自动生成代码工具

背景

很多业务很多功能对应的操作都很接近,很多代码都是共通的,只需改动一部分。如果新建一张表,共通的代码能自动生成就好,不需要拷贝原来的代码还要修改,能节省很多时间。

准备

1、MyBatis使用Generator自动生成代码,工具配置、数据库连接读取等通过它来完成。需要下载mybatis-generator-core-1.3.2

MyBatis生成的是固定格式的的几个模板,而且只能是java相关的代码,并且很多东西还不太灵活,不能满足很多业务。

2、通过velocity生成模板文件。需要下载velocity1.7

设计

1.一个表对应的数据通过MyBatis读取。

2.按一定规则自定义一套模板文件,通过velocity生成每个表对应的模板文件。自定义的模板文件可以满足PHP/JAVA等各种语言的代码生成。

开发语言:java

详细说明及源码

正准备开发中,待续!

 

 

StarCraft开发:用肮脏的技巧解决难题

发表于2013-02-28 16:15| 7145次阅读| 来源Code of Honor| 53 条评论| 作者Patrick Wyatt

摘要:在之前的文章中,Warcraft之父讲述了自己是如何以及为何重启StarCraft的开发,在“离终止日期仅剩下两月”的压迫下,开发团队不得不做出了很多错误的决定,以至于带来了众多遗留问题。在本文中,Patrick讲述了其中一个难题——路径寻找,以及他最终是如何通过肮脏的技巧来解决的。

游戏单位的路径寻找功能是很多玩家从未关注的东西,除非它存在巨大的问题,然后这个并不重要的问题会引发玩家更大的愤怒,最终导致“毁灭世界”的大灾难。在StarCraft的开发过程中,很多时候其路径寻找功能完全不起作用。

随着StarCraft开发的延长,它看起来像是个不会完工的项目:永远离发布还有两个月,但没有认识征兆显示正在接近传说中的发布日期。“幸运”——我有意地使用这个词——的是,Blizzard之前就有拖延游戏发布的经验。

我们一直都有“目标”(尽管用“期望”也许更好) 发布日期,但除非游戏已经准备完毕,否则我们并不打算发布它。Blizzard“静待完善”的政策意味着承诺只发布高质量产品,也意味着无法保证游戏的发布日期。

无论如何,若想发布最终 产品,我们还需要解决一系列难题。和其他游戏游戏一样,在开发的收官阶段总需要寻找并修补各式各样的缺陷,其数量可以千记。

有的只是小问题,只需稍作修补;不幸的是,并非所有缺陷都是如此。

其它的,比如之前提到的多人同步bug,会需要编程团队多个成员持续工作数周来解决。其他程序员,比如 Ages of EmpiresSupreme Commander的开发者,也报告过类似的同步bug经历。

有的bug与开发过程本身相关。Protoss Carrier(神族的航空母舰)相比于其他单位总是有延迟,因为它处理任何事情的方式与众不同。在某个时间点,Carrier的控制从游戏代码主干中分支出来,然后在重新集成时发生了预料之外的问题,因此任何其它单位新增的功能都需要为Carrier重新实现;每次其它单位中修复的bug,也会在Carrier发现类似bug,不过修复起来更加麻烦。

但拖延StarCraft发布的最大问题还要数路径寻找问题。

路径寻找功能并非完全坏掉的,恰恰相反,大多数情况下表现得非常好,但是很多边界情况下的问题导致游戏迟迟无法发布。

游戏单位常常会卡住,在战场上停下来。大多数情况下,它们最终能够找到正确的路径,缓慢地向前进或者在周围旋转;但有时候会越来越挤,最终无法再前进一步,整个任务会陷入上下班一样的堵塞状况。

这个问题让玩家感到沮丧,也让AI变得虚弱,经常无法平衡任务并且浪费设计时间。

虽然我并非顶级RTS玩家,但是在游戏发布前我还是算擅长的,因为我发现Goliaths(人族巨人)因为差劲的路径寻找问题被过度加强了,因此通过小心指引Goliaths绕过障碍,我就能在关键时刻顶住火力,战胜“宏”玩家。可惜的是,Goliaths被平衡后我的技巧就不再起作用了。sigh

早期的路径寻找功能非常粗糙——虽然有精选的算法指引单位运动,但是项目过程中一些糟糕的决定导致其天生残疾。

我们怎么会在这里?


之前的一篇文章里,我提到了路径寻找难题。StarCraft使用了Warcraft的游戏引擎,使用tile引擎来渲染地形,而这个tile引擎为处理16个8×8组成的32×32方形单元做了专门的优化。我在Warcraft中设计这样的架构是因为:之前为超级任天堂和Genesis开发游戏时都运作良好。这些游戏机有专门绘制8×8tile的硬件支持,而这些功能在PC上也容易模拟。

因为Warcraft I和II和的视角近乎是自上而下,游戏地图上所有物体(森林、地形、建筑)的边缘都是是水平或者垂直,因此渲染引擎将世界当作正方形tile地图来渲染有益于路径寻找,在这两个游戏中,每个32×32的像素块要么可通过要么不可通过。如下图,我将可通过的tile注以绿色边缘。图中有的tile显示为可通过,实际上不可以;在下方你能看到兵营建筑的图形就不能 填满它所坐落的48×48区域,剩下两个看起来可通过,实际上不可通过的tile。

tile为32×32的Warcarft 2地图

但是,为了将StarCraft变得更有视觉吸引力(详情请见《StarCraft: Orcs in Space 在欺骗中浴火重生》),开发团队将其切换为对角形式。但是底层的地形引擎并没有为对角tile重新设计,仅仅重新绘制了tile。

新视角看起来非常不错,但为了保证路径寻找的正常工作,必须增加路径寻找地图的分辨率:现在精细为16个8×8的tile。得益于更高的分辨率,地图上也可以呈现出更多的单位,这也意味着为了搜寻更大的路径空间需要花费更多的计算能力。

现在路径寻找更具挑战性,因为tile的对角线边缘将很多正方形tile不均等地分割了开来,这样确定一个一个tile可通过或者不可通过变得更加困难。为了保证所有的tile标记的正确,需要更多的计算工作。

StarCraft地图编辑器非常难写,因为很多为了将对角图形放置在方形地图上带来了很多边界情况。编写特制的“tile修复”代码至少需要数月时间。

和使用了对角渲染引擎的Diablo不同,StarCraft开发团队不得不继续使用旧引擎,尽管每周都会发现更多的新问题。

这张图显示了一座桥是如何以8×8的拼;tile组成的,有几个标注为了绿色。透视角度的贴图不均等地覆盖在tile上,这导致大桥的两边都出现了楼梯一样的边缘,正如你在图中所见,红线将每个tile都切割为不规则的形状。

使用8×8tile的StarCraft

因为项目一直受到离发布仅剩两个月的压力,所以不可能为了让路径寻找变得更容易而重新设计地形引擎,也因此路径寻找功能一直难以完善。为了解决棘手的边缘问题,路径控制代码成为了一个巨大的状态机,用来处理各种专门的“让我离开这里”的命令。

高峰时间


如果说路径寻找问题有一个最严重的情况,那一定是采伐单位(人族SCV,虫族drone,神族probe),它们在采集水晶或者vespene gas(之后的矿物“minerals”)会产生严重的堵塞,最后慢慢停止。当玩家在忙于管理进攻或者第二基地时,基地的采伐单位就会堵塞,因此金库不再有收入,然后玩家会发现因为没有足够的金钱而无法继续建造。

资源采集的基本问题在于,玩家希望最大化每个矿源采伐者的数目以获得最多的金钱。采伐者会经常来往于矿产与基地之间,所以经常会和其他采伐者相向而行,并且“撞”在一起。一个狭小的空间涌入了过多的采伐者,往往会导致其中大部分会阻塞在一起直到矿藏被采伐完。

我们如何从这里出去?


我记不清那时是自愿还是被要求来解决这个问题了,在阅读了大部分的路径寻找代码后,我明白了自己并没有那么聪明以至“解决这个难题”,因此我想出了一个肮脏的小把戏。

程序员经常会沉迷于寻找最纯粹、简洁甚至崇高、神圣的解决方案,但在项目中必须有所舍弃。如果解决得很好,没有人会注意到实现背后邪恶的妥协,正如Brandon Sheffield在《Dirty Coding Tricks》一文中所写的那样。

我的想法很简单,只要采伐者往采矿的方向前进,或者是往回运送矿物时,它们会自动忽略与其它单位的碰撞。通过消除采伐者单位间碰撞的代码,运输过程再也不会遇到阻塞的状况,因此采伐者能够高效地运作。

如果你选中大群正在采集水晶的采伐者并命令其停下来,你会发现他们 会立刻展开、寻找没有被其它单位占领的tile。

如果你仔细观察的话会发现这很明显,但它就是在你的眼前隐藏了——并没有触发你视觉意识层面,当然专业玩家和地图开发者/MOD开发者除外。

简而言之,它就是管用,这就是最好的技巧。

虽然,想要完成游戏还有很多工作要做,但是这次技巧让我们免于重新开发游戏引擎,节约了很多时间。

其它单位的路径寻找问题开发团队已经能够胜任了,因此没什么可说的,尽管Protoss Dragoon(神族龙骑士)是个例外,因为它作为最大的地面单位,还是经常会出现路径寻找问题。

最终结果是,路径寻找已经足够胜任,我们也从中学到了沉重的教训。

原文链接: Code of Honor

相关文章:

StarCraft开发的荆棘之路

StarCraft开发:如何避免链表引起的游戏崩溃

StarCraft: Orcs in Space 在欺骗中浴火重生

较新的文章

Copyright © 2024 优大网 浙ICP备13002865号

回到顶部 ↑