编程思想能够分为如下几个大类: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,那样没有意义。