編程思想能夠分爲如下幾個大類:javascript
原則(Principles)css
範式(Paradigms)html
方法論(Methodologies)java
模式(Patterns)python
我認識(或者說如今想得起來的)的原則主要有如下幾種:linux
DRY (Don't Repeat Yourself)程序員
OCP (Open Close Principle)web
SoC (Separation of Concerns)算法
IoC (Inversion of Concerns)django
CoC (Configuration over Convention)
Indirection (Layering)
"Don't repeat yourself"很好理解。當你第二次寫一樣結構,變化不大的代碼時,腦殼裏就要閃現一個大大的問號:我是否是在repeat myself?若是是,就要重構,或封裝,或抽象,或函數化,總之一個目的,消除重複。以筆者的經驗,DRY原則看似基本,實則不少大型軟件公司都未能作好,copy & paste處處可見。咱們寫代碼,從一開始就要把握好這個原則,不然在「破窗理論」的指引下,代碼的質量會快速划向萬劫不復的深淵。
OCP原則是說「軟件要對擴展開放,對修改封閉」。好比你寫一個message dispatching的代碼,若是你只用一個主函數去處理全部消息,那麼,每加一個message type,你就須要修改這個函數使之能處理新的消息。正確的,使用了OCP原則的代碼是每一個消息都有本身的callback,主函數僅僅根據消息的類型找到對應callback,而後執行。這樣,新加的任何消息都無需改動主處理函數。這就是「對擴展開放,對修改封閉」的一個最淺顯的例子。軟件開發中的不少手段,如繼承,如Observer pattern(觀察者模式)目的就是實現OCP原則。
以上兩個原則是最基礎最基礎的原則,以後的原則都是在此基礎上衍生出來的。
SoC聽起來高大上,其實就是解耦,將複雜系統分治成不一樣的子系統,儘量將變化控制在子系統中。若是你十多年前作過互聯網,就知道那時的html混雜着語義和樣式,牽一髮而動全身;如今的網站html/css基本分離,上帝的歸上帝,撒旦的歸撒旦,各司其職。這就是SoC。另外一個SoC的經典應用場景就是MVC design pattern —— 整個系統的邏輯被分紅 Model,View,Controller三層,(理想狀態下)其中一層的改動不會影響到另外一層。
IoC原則的思想是"Don't call me, I'll call you"。這一原則促使軟件產業從lib時代發展到framework時代。想一想libc,裏面有各類各樣的函數供你驅使,整個控制權在你;再看看django這樣的framework,你並無整個系統的控制權,你只能被動地按照規範寫出一個個函數或類,在必要的時候由framework調用。使用IoC原則的好處是高級的細節和邏輯被隱藏,開發者只須要關注business logic。好比說使用ChicagoBoss(erlang的一個 web framework)來寫web app,你寫的代碼基本上是順序的,併發(concurrency)無關的,但整個系統的執行是異步的,大量併發的。
CoC原則出自Rails(或者至少Rails將其發揚光大),它的意思是:爲了簡單起見,咱們寫代碼按照必定的約定寫(代碼放在什麼目錄,用什麼文件名,用什麼類名等),這樣省去了不少沒必要要的麻煩(但也不失flexibility,由於約定能夠經過配置修改),好比說在ember裏component的定義在controller裏要CamelCase,在template裏要用"-"。在django裏,只要你在"app/management/commands"裏寫一個文件,繼承BaseCommand類,就能夠經過"./manage.py xxx"運行你的命令。
Indirection/Layering原則也是爲了解耦,就是把系統分紅不一樣的層次,嚴格規定層次間的調用關係。layering最著名的例子是ISO/OSI七層模型;indirection最著名的例子是hypervisor。軟件領域最著名的一句話是:"All problems in computer science can be solved by another level of indirection."
講完了原則,講講範式。我能想到的兩個範式是:
GP: Generic Programming
MP: Meta Programming
不少人一看到GP(泛型編程)就想到C++中的template,想到STL。此GP非彼GP也。這裏的泛型編程是從抽象度的角度來看問題 —— 即對算法和算法操做的數據進行解耦。舉個例子,咱們要計算一個字符串表達式的值:"3* 20 * 7 * 48"。這用python不難實現:
s = "3* 20 * 7 * 48" def calc(s, sep): r = 1 for t in s.split(sep): if t != "": r *= int(t) return r calc(s, "*") 20160
若是s是個加法的表達式呢?更好的方式是:
s = "3* 20 * 7 * 48" def calc(s, sep): op = {'*': operator.mul, '+': operator.add, ...} return reduce(op[sep], map(int, filter(bool, s.split(sep)))) calc(s, "*") 20160
在這個實現裏,算法被抽象出來與數據無關。
再好比下面這個函數,對給定的list裏面的任何一個元素執行一個測試,若是測試經過,則執行action,返回執行結果的list。
def process(l, test, action): def f(x): return action(x) if test(x) else None return filter(None, map(f, l))
這個函數能夠應用於不少場景,好比說:「從公司的directory裏找到全部女性工程師,將她們的工資統一漲10%」,「給我本身的微博裏全部在北京的粉絲髮一條消息」這樣兩個看似徹底無關的場景。最重要的是,process函數一旦寫完,就基本不須要任何改動而應用於這兩個(甚至更多的)場景。從這裏也能夠看出,GP的一個做用就是實現OCP原則。
以上所述原則和範式都與具體的語言無關,是能夠放之四海而皆準的基本思想。但Metaprogramming否則。它跟語言的能力頗有關係。
狹義的metaprogramming指代碼可以將代碼看成數據操做,廣義講就是在接近語言級的層面寫的讓代碼更具動態性的代碼。先舉一個後者的例子:
class dotdict(dict): def __getattr__(self, attr): return self.get(attr, None) __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ dd = dotdict({'a': 1, 'b': 2}) dd.a 1
在python裏,訪問字典須要使用"[]",但咱們可使用語言自身的魔法函數(magic functions)將"."操做與"[]"映射起來,達到使用"."來訪問字典的效果(就像javascript同樣)。字典裏的key是無限延伸的,你沒法對每一個key生成一個方法,但求助於語言層的能力,就能夠作到這一點。同理,若是讓你寫一個微博的api的sdk,你沒必要爲每個api寫一個方法,一個__getattr__
就能夠將全部api抽象統一。這就是廣義的metaprogramming,讓代碼更具動態性。
狹義的metaprogramming用django的ORM來講明最好:
class TagItem(models.Model): class Meta: app_label = 'cayman' verbose_name = verbose_name_plural = _('關聯的實體') tag = models.ForeignKey('Tag', verbose_name=_('分類'), null=True) content_type = models.ForeignKey(ContentType, default=None, blank=True, null=True) object_id = models.PositiveIntegerField(blank=True, null=True) item = generic.GenericForeignKey('content_type', 'object_id')
複雜的Object relational mapping以這樣一種declarative的方式解決了(你甚至能夠將它當作一種DSL,Domain Specific Language),若是沒有metaprogramming的支持,想一想你如何以如此優雅地方式實現這種mapping?
固然,就metaprogramming的能力而言,把代碼徹底看作數據(列表展開與求值)的lisp族語言更甚一籌。這是我爲什麼說metaprogramming的能力和語言相關。我沒有真正寫過lisp代碼(clojure僅僅寫了幾個hello world級的函數),但聽說lisp程序員寫一個系統時,會先寫一個針對該系統的DSL,而後再用這個DSL寫代碼。據說而已,我沒有親見。
主流的方法論不外乎三種:
OOP(Object Oriented Programming)
AOP(Aspect Oriented Programming)
FP(Functional Programming)
OOP就不在這裏討論了,這是一個已經被說爛了的名詞。注意OOP是一種思想,和語言是否支持無關。不支持OOP的C同樣能夠寫出OOP的代碼(請參考linux kernel的device),支持OOP的python也有不少人寫出來過程化的代碼。
AOP是指把輔助的關注點從主關注點中分離,有點SoC的意味。在django裏,咱們會寫不少view,這些view有各自不一樣的邏輯,但它們都須要考慮一件事:用戶登陸(得到受權)後才能訪問這些view。這個關注點和每一個view的主關注點是無關的,咱們不應爲此分心,因而(爲了簡便起見,如下我使用了django裏已經逐漸廢棄的function based view):
@login_required def user_view(request, *args, **kwargs): user = request.user profile = user.get_profile() ...
這裏,login_required
這個decorator就是AOP思想的一個很好的例子。
不少時候AOP是OOP的一個用於解耦的補充。
OOP發展了這麼多年,慢慢地觸及了它固有的天花板 —— 爲了容納更多的業務,不斷抽象,不斷分層,最終超過了人腦所能理解的極限。儘管有些design patterns努力幫咱們把縱向的金字塔結構往橫向發展(如composite pattern,decorator pattern等),但依然改變不了OOP樹狀的,金字塔型的結構。
若是說OOP幫助你構建層級式的系統,那麼FP(函數式編程)則反其道而行之:在FP的世界裏,一切是平的。你要構建的是一個個儘量抽象的函數,而後將其組織起來造成系統。
好比說要你作一個系統,實現對list的合併,若是你是個OOP的好手,你可能這麼作:
class Base(object): def __init__(self, l): self.l = l def reduce(self): raise NotImplemented class Adder(Base): def reduce(self): n = 0 for item in self.l: n += item return n class Multipler(Base): ...
但對於FP,你大概會這麼作:
def list_op(f): def apply(l): return reduce(f, l) return apply adder = list_op(operator.add) multipler = list_op(operator.mul)
函數式編程經過變化,組合各類基本的函數可以實現複雜的功能,且實現地很是優雅。如咱們前面舉的例子:
return reduce(op[sep], map(int, filter(bool, s.split(sep))))
這種兼具可讀性和優雅性的代碼表明瞭代碼撰寫的將來。咱們再看一個例子(haskell):
boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
即便你沒學過haskell,你也能當即領會這段代碼的意思。
函數式編程有部分或所有以下特色(取決於語言的能力):
Immutable data
First-class functions
Higher-order functions
Pure functions (no side effects)
Recursion & tail recursion
Iterators, sequences
Lazy evaluation
curry
Pattern matching
Monads....
其中很多思想和目前的多核多線程場景下進行高併發開發的思想契合。因此你會看到erlang,haskell這樣的語言愈來愈受到重視,並被用到各類生產環境。
模式是在系統開發的過程當中反覆被用到的一些解決具體問題的思想。設計模式(Design patterns)首先由GoF(Gang of Four)總結,而後在Java中發揚光大。其實隨着語言的進化,很多模式已經被整合在語言當中,好比iterator,有些已經固化到你寫代碼的方式當中,好比bridge,decorator,有些在framework裏在不斷使用而你不知道,如經典的MVC,如django的middleware(chain of responsibility),command(command pattern)等等。時間關係,就不一一道來。
最後,寫代碼是爲了解決問題,而不是秀肌肉。腦殼裏有了大原則,那麼範式,方法論,模式這些實現手段哪一個順手,哪一個更好地能解決問題就用哪一個。代碼寫出來首先要爲功能服務,其次爲可讀性服務,不是爲某個思想服務的,也就是說,不要爲了OO而OO,不要爲了MP而MP,那樣沒有意義。