Showing posts with label Design Principles. Show all posts
Showing posts with label Design Principles. Show all posts

Thursday, February 4, 2016

Law Of Demeter - Least Knowledge Principle



http://zhangyi.xyz/demeter-law-and-refactoring
https://www2.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf
这个原则认为,任何一个对象或者方法,它应该只能调用下列对象:
  • 该对象本身
  • 作为参数传进来的对象(也可以是该对象的字段)
  • 在方法内创建的对象

这个原则用以指导正确的对象协作,分清楚哪些对象应该产生协作,哪些对象则对于该对象而言,又应该是无知的。如何理解这个原则?我们可以看看David Bock就该原则给出的一个绝佳案例。假设一个超市购物的场景,顾客(Customer)到收银台结账,收银员(Paper Boy)负责收钱
让我们将PaperBoy中charge()方法的代码翻译成这幕小话剧的对白。
“把钱包交出来!”收银员算出顾客要买的商品总价后,“温柔”地对顾客说道。
顾客言听计从,赶紧将钱包掏出来,恭恭敬敬地递给收银员。
接过钱包,收银员毫不客气地打开,检查里面的钱够不够。噢,不错,钱够了。收银员从钱包取出钱,心满意足地笑了。
判断一段代码是否违背了迪米特法则,有一个小窍门,就是看调用代码是否出现形如a.m1().m2().m3().m4()之类的代码。这种代码在Martin Fowler《重构》一书中,被名为“消息链条(Message Chain)”,有人更加夸张地名其为“火车残骸”。车祸现场啊,真是惨不忍睹。
那么,如下代码是否这样的残骸呢?
1
2
3
4
5
str.split("&")
 .stream()
 .map(str -> str.contains(elementName) ? str.replace(elementName + "=", "") : "")
 .filter(str -> !str.isEmpty())
 .reduce("", (a, b) -> a + "," + b);
不是的。这样的代码我们一般称之为“流畅接口或连贯接口(Fluent Interface)”。二者的区别在于观察形成链条的每个方法返回的是别的对象,还是对象自身。如果返回的是别的对象,就是消息链条。所谓m1().m2().m3().m4()的调用,其实是调用者不需要也不想知道的“知识”,把这些中间过程的细节暴露出来没有意义,调用者关心的是最终结果;而上述代码中的map()filter()等方法其实返回的还是Stream类。这一调用方式其初衷并非告知中间过程的细节,而是一种声明式的DSL表达,调用者可以自由地组合它们
http://blog.csdn.net/zhengzhb/article/details/7296930
定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
         自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
         迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
         举一个例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。
        现在这个设计的主要问题出在CompanyManager中,根据迪米特法则,只与直接的朋友发生通信,而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。修改后的代码如下:
class CompanyManager{
 public List<Employee> getAllEmployee(){
  List<Employee> list = new ArrayList<Employee>();
  for(int i=0; i<30; i++){
   Employee emp = new Employee();
   //为总公司人员按顺序分配一个ID
   emp.setId("总公司"+i);
   list.add(emp);
  }
  return list;
 }
 
 public void printAllEmployee(SubCompanyManager sub){
  sub.printEmployee();
  List<Employee> list2 = this.getAllEmployee();
  for(Employee e:list2){
   System.out.println(e.getId());
  }
 }
}
        修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。
        迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
迪米特法则:Law Of Demeter,LoD。
也被称为最少知识原则,Least Knowledge Principle,LKP。
就是说一个对象应该对其他对象保持最少的了解。正如最少知识原则这个定义一样,一个类应该对其耦合的其他类或所调用的类知道得最少。所耦合的类内部无论如何复杂,怎么实现的我都不需要知道,我只调用你public出来的这些方法,其他都不用知道。
看到这里很多人都会明白,这种场景在实际开发中是非常常见的一种情况。对象A需要调用对象B的方法,对象B有需要调用对象C的方法……就是常见的getXXX().getXXX().getXXX()……类似于这种代码。如果你发现你的代码中也有这样的代码,那就考虑下是不是违反迪米特法则,是不是要重构一下了。
  • 类A只与最直接的朋友类B通信,不与类C通信;
  • 类A只调用类B提供的方法即可,不用关心类B内部是如何实现的(至于B是怎么调用的C,这些A都不用关心)。
迪米特法则的目的是让类之间解耦,降低耦合度。只有这样,类的可复用性才能提高。
但是迪米特法则也有弊端,它会产生大量的中转类或跳转类,导致系统的复杂度提高。
所以我们不要太死板的遵守这个迪米特法则,在系统设计的时候,在弱耦合和结构清晰之间反复权衡。尽量保证系统结构清晰,又能做到低耦合。
The Law of Demeter (LoD) or principle of least knowledge is a specific case of loose coupling.
  • Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
  • Each unit should only talk to its friends; don't talk to strangers.
  • Only talk to your immediate friends.
The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents), in accordance with the principle of "information hiding".
a module should not know about the inner details of the objects it manipulates. If a code depends upon internal details of a particular object, there is good chance that it will break as soon as internal of that object changes. 

One mistake many Java programmer makes it exposing internal detail of object using getter methods and this is where principle of least knowledge alerts you.

In short, the Law of Demeter aims to keep you from doing things like this:
objectA.getObjectB().getObjectC().doSomething(); ===> contrvsery

When you write code like this, not only are you exposing yourself to changes in the ObjectA class, you're also exposing yourself to changes that may occur in ObjectB and ObjectC as well.
Forces
  • Your classes will be "loosely coupled"; your dependencies are reduced.
  • Reusing your classes will be easier.
  • Your classes are less subject to changes in other classes.
  • Your code will be easier to test.
  • Classes designed this way have been proven to have fewer errors.
Exception 1: Data Structures
Exception 2: The Builder Pattern and Other Fluent APIs

When an API is designed to be fluent (and therefore, usually chained), there’s no good reason to lose the readability just to be a strict Law of Demeter follower. For example, java 8’s Stream API would be worthless if you didn’t allow yourself to chain methods.
http://www.dan-manges.com/blog/37
Models should define business logic and be able to stand alone from views.
http://c2.com/cgi/wiki?LawOfDemeter

Practice
https://medium.com/@orhanobut/yet-again-law-of-demeter-4a7e8fa7707d
  private void showAvatar(){
    Image image = user.getAccount().getImage();
  }
==>
public class User { ...
public Image getAvatar(){
if (account == null) {
return;
}
account.getImage();
}
}
public class Foobar {
public void showAvatar() {
Image image = user.getAvatar();
}
}
http://blog.csdn.net/vking_wang/article/details/8455636
又称最少知识原则(Least Knowledge Principle),一个对象应该对其他对象有最少的了解
一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。

        类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
        迪米特法则包含4层含义:
        1)只和朋友交流
        Only talk to your immediate friends.两个对象之间的耦合就成为朋友关系。即,出现在成员变量、方法输入输出参数中的类就是朋友;局部变量不属于朋友。
--> 不与无关的对象发生耦合!
        方针:不要调用从另一个方法中返回的对象中的方法!只应该调用以下方法:
  • 该对象本身的方法
  • 该对象中的任何组件的方法
  • 方法参数中传入的对象的方法
  • 方法内部实例化的对象的方法
        例如:Teacher类可以命令TeamLeader对Students进行清点,则Teacher无需和Students耦合,只需和TeamLeader耦合即可。
反例:
public float getTemp(){
     Thermometer t = station.getThermometer();
     return t.getTemp();
}
客户端不应该了解气象站类中的温度计对象;应在气象站类中直接加入获取温度的方法。改为:
public float getTemp(){
     return station.getTemp();
}


        2)朋友间也应该有距离
        即使是朋友类之间也不能无话不说,无所不知。
--> 一个类公开的public属性或方法应该尽可能少!
        3)是自己的就是自己的
        如果一个方法放在本类中也可以、放在其他类中也可以,怎么办?
--> 如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,就放置在本类中。
        4)谨慎使用Serializable
        否则,若后来修改了属性,序列化时会抛异常NotSerializableException。
建议:
        迪米特法则的核心观念是:类间解耦。
        其结果是产生了大量中转或跳转类。

Dependency Inversion Principle - SOLID principles



http://blog.csdn.net/zhengzhb/article/details/7289269
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
         依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
         依赖倒置原则的核心思想是面向接口编程
Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴,他们各自都去实现IReader接口,这样就符合依赖倒置原则了,代码修改为:
class Newspaper implements IReader {
 public String getContent(){
  return "林书豪17+9助尼克斯击败老鹰……";
 }
}
class Book implements IReader{
 public String getContent(){
  return "很久很久以前有一个阿拉伯的故事……";
 }
}

class Mother{
 public void narrate(IReader reader){
  System.out.println("妈妈开始讲故事");
  System.out.println(reader.getContent());
 }
}

public class Client{
 public static void main(String[] args){
  Mother mother = new Mother();
  mother.narrate(new Book());
  mother.narrate(new Newspaper());
 }
}
    这样修改后,无论以后怎样扩展Client类,都不需要再修改Mother类了。这只是一个简单的例子,实际情况中,代表高层模块的Mother类将负责完成主要的业务逻辑,一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。
    采用依赖倒置原则给多人并行开发带来了极大的便利,比如上例中,原本Mother类与Book类直接耦合时,Mother类必须等Book类编码完成后才可以进行编码,因为Mother类依赖于Book类。修改后的程序则可以同时开工,互不影响,因为Mother与Book类一点关系也没有。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。现在很流行的TDD开发模式就是依赖倒置原则最成功的应用。
         传递依赖关系有三种方式,以上的例子中使用的方法是接口传递,另外还有两种传递方式:构造方法传递setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。
在实际编程中,我们一般需要做到如下3点:
  • 低层模块尽量都要有抽象类或接口,或者两者都有。
  • 变量的声明类型尽量是抽象类或接口。
  • 使用继承时遵循里氏替换原则。
        依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

Sunday, September 27, 2015

How to Build Robust Application



https://en.wikipedia.org/wiki/Robustness_principle
Be conervative in what you do, be liberal in what you accept from others (often reworded as "Be conservative in what you send, be liberal in what you accept").

In tcp: In other words, code that sends commands or data to other machines (or to other programs on the same machine) should conform completely to the specifications, but code that receives input should accept non-conformant input as long as the meaning is clear.

容错技术介绍
容错按系统级别划分,分为三个级别,硬件容错、软件容错以及系统容错。硬件容错常用的方法包括使用冗余、多备份技术、增加内存、能源系统冗余等。硬件错误通常能够够在两个物理机上进行隔离处理。软件容错主要是正对软件的鲁棒性特征进行增强。常见的方法有checkpoint/restart,recovery blocks,N-Version Programs等。对于系统容错,设计一个独立与目标系统的子系统,通过定义定义规则来容忍系统缺陷。对缺陷的处理,有以下几类技术:

  1. 使用缺陷避免技术来避一些错误。使用成熟的设计方法论、验证以及确认方法论、代码检查、上线前的演练等。
  2. 在可能会存在的缺陷时,可以选择缺陷移除技术,例如测试、集成测试、回归测试、背靠背测试等;
  3. 或者是在遭遇错误是,缺陷回避的方式,是的潜在的缺陷不会被激活。常见技术是通过重新配置系统来达到避免的目标;
  4. 缺陷容忍技术,系统能够对缺陷进行侦测、诊断、孤立、覆盖、不错、以及系统恢复。使用以上多种技术混合。

2. Graceful Degradation

在系统遭遇某个错误不能提供完整功能,系统可以降低自己服务能力。
上述backup()方法中while循环中waitpid需要等待到child创建成功。可能永远不会成功,需要改进。

3. Selective Retry


重复调用仅当有机会重试成功。例如,内存短缺消失;主要针对突然地高负载资源短缺。它能够增加资源分配成功的可能性



Saturday, September 19, 2015

The Art of Unix Programming



http://www.faqs.org/docs/artu/index.html
http://www.faqs.org/docs/artu/ch01s06.html
Rule of Modularity: Write simple parts connected by clean interfaces.

Rule of Clarity: Clarity is better than cleverness.

Rule of Composition: Design programs to be connected to other programs.

Rule of Separation: Separate policy from mechanism; separate interfaces from engines.

Rule of Simplicity: Design for simplicity; add complexity only where you must.

Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.

Rule of Transparency: Design for visibility to make inspection and debugging easier.
A software system is transparent when you can look at it and immediately understand what it is doing and how. It is discoverable when it has facilities for monitoring and display of internal state so that your program not only functions well but can be seen to function well.

For a program to demonstrate its own correctness, it needs to be using input and output formats sufficiently simple so that the proper relationship between valid input and correct output is easy to check.

Rule of Robustness: Robustness is the child of transparency and simplicity.

Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.

Rule of Least Surprise: In interface design, always do the least surprising thing.
-- Principle of Least Astonishment

Rule of Silence: When a program has nothing surprising to say, it should say nothing.

Rule of Repair: When you must fail, fail noisily and as soon as possible.
write your software to cope with incorrect inputs and its own execution errors as gracefully as possible. But when it cannot, make it fail in a way that makes diagnosis of the problem as easy as possible.
Consider also Postel's Prescription: “Be liberal in what you accept, and conservative in what you send”. Postel was speaking of network service programs, but the underlying idea is more general. Well-designed programs cooperate with other programs by making as much sense as they can from ill-formed inputs; they either fail noisily or pass strictly clean and correct data to the next program in the chain.

Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.

Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.

Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
90% of the functionality delivered now is better than 100% of it delivered never
Premature optimization is the root of all evil
Prototype, then polish. Get it working before you optimize it. Or: Make it work first, then make it work fast. 
Make it run, then make it right, then make it fast”.
get your design right with an un-optimized, slow, memory-intensive implementation before you try to tune. Then, tune systematically, looking for the places where you can buy big performance wins with the smallest possible increases in local complexity.

Rule of Diversity: Distrust all claims for “one true way”.

Rule of Extensibility: Design for the future, because it will be here sooner than you think.
 it's even more foolish to believe them about your own designs. Never assume you have the final answer. Therefore, leave room for your data formats and code to grow; otherwise, you will often find that you are locked into unwise early choices because you cannot change them while maintaining backward compatibility.
When you design protocols or file formats, make them sufficiently self-describing to be extensible. Always, always either include a version number, or compose the format from self-contained, self-describing clauses in such a way that new clauses can be readily added and old ones dropped without confusing format-reading code.


Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts