六大设计原则
单一职责原则Single Responsibility Principle,简称SRP,就一个类而言,应该仅有一个引起它变化的原因。 同价位的相机和手机哪个拍照好?我觉得说同价位都太谦虚了,低端的千元卡片机完全可以吊打比自身贵至少三五倍价钱的手机,如果是万元单反,我觉得市场上已经没有什么手机的拍照效果可以与之相抗了。 ? 整合当然是一种很好的思想,是时代发展的主方向,但是我们在进行程序设计的时候,更应该要在类的职责分离上多思考,做到单一职责,这样的代码才容易维护与复用。 单一职责的好处1.类的复杂性降低。 很难实践然而,这个设计原则饱受争议,很难在项目中得到体现。 原因在于,职责边界很难划分,每个人的看法不同,并且随着项目功能的不断迭代,开发人员必须要做出相应的妥协,想要保持类的单一职责几乎不可能,否则就会冗余出非常多的类文件。 ? 而我们设计类的时候应该做的是:接口一定要遵守单一职责原则,实现类尽量遵守单一职责原则。 里氏替换原则Liskov Substitution Principle,里氏替换原则,简称LSP,任何基类可以出现的地方,子类一定可以出现。 LSP原则是继承复用的基石。 武器设计我们都看过电影里动作演员打架,他们的武器有各种各样的刀、剑、暗器等等。。。 根据里氏替换原则,类图就可以这样设计: 程序示例武器的抽象类 public abstract class AbstractWeapon { //攻击 public abstract void attack(); }
public class Knife extends AbstractWeapon { @Override public void attack() { System.out.println("用刀发起普通攻击"); } } ? 剑 public class Sword extends AbstractWeapon { @Override public void attack() { System.out.println("用剑发起普通攻击"); } } ? 人物类 public class Person { private AbstractWeapon weapon;//武器 public Person(AbstractWeapon weapon) { this.weapon = weapon;//初始化给一把武器 } //发起攻击 public void attack(){ weapon.attack(); } } ? 调用 public static void main(String[] args) { Person a = new Person(new Knife()); a.attack(); Person b = new Person(new Sword()); b.attack(); } ? 输出: 刀发起普通攻击 ? 示例中代码的核心在于使用父类做为参数,那么子类不管什么武器都可以传入,里氏替换原则的目的就是增强程序的健壮性,对业务的横向扩展有着很好的支持。 ? 依赖倒置原则Dependence Inversion Principle,依赖倒置原则,简称DIP, a.高层次的模块不应该依赖低层次的模块,他们都应该依赖于抽象。 b.抽象不应该依赖于具体,具体应该依赖于抽象。 这话说简单点就是要针对接口编程,不要针对实现编程。 比方说电脑的鼠标、键盘都是针对接口设计的,如果针对实现来设计的话,那么鼠标就要对应到具体的哪个品牌的主板,买个鼠标得去研究是否适用于当前电脑主板的型号。 优化武器程序既然要针对接口编程,那么我们就稍微改一下上面的程序,给Person加个抽象(我这里使用的是抽象类,也可以使用接口,都符合原则) ? 人物抽象 public abstract class AbstractPerson { protected String name;//每个人都有名字 protected AbstractWeapon weapon;//武器 //创建人物 public AbstractPerson(String name,AbstractWeapon weapon) { this.name = name; this.weapon = weapon; } public abstract void attack();//攻击 } ? 人物 public class Person extends AbstractPerson{ public Person(String name,AbstractWeapon weapon) { super(name,weapon); } //发起攻击 public void attack(){ System.out.print(this.name + "上场,"); this.weapon.attack(); } } ? 调用 public static void main(String[] args) { AbstractPerson a = new Person("张三",new Knife()); a.attack(); AbstractPerson b = new Person("李四",new Sword()); b.attack(); } ? 输出: 张三上场,用刀发起普通攻击 ? 细细看来,在main方法里依赖的就是AbstractPerson这个抽象了,在Person类里依赖的也是AbstractWeapon抽象,完全符合依赖导致原则。 ? 1.每个类尽量都有接口或抽象类。 2.变量的表面类型尽量是接口或抽象类。 ? 接口隔离原则Interface Segregation Principle,接口隔离原则,简称ISP,客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。 简单点说,就是要细化接口,接口的方法尽量少。如果一个接口为多个模块提供访问,那么这个接口就应该进行拆分。 ? 接口隔离原则实际上也很难得到体现,接口的粒度越小,就越是隔离,系统越灵活,但是,系统结构越是复杂,开发难度增加,可维护性降低,所以,遵守接口隔离原则需要一个度。 ? 迪米特法则Law of Demeter,迪米特法则,又叫做最少知识原则(Least Knowledge Principle),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。 迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。 ? 迪米特法则的核心观念就是类与类间要解耦, ? 开闭原则先来看看开闭原则的定义:一个软件实体(类、模块、函数等等)应该对扩展开放,对修改关闭。 这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为,做到真正的拥抱变化。 武器展览那我们就举例说明一下什么是开闭原则,就以武器作为原型故事吧 ? 武器通用接口 public interface IWeapon { String getName();//每个武器都有名称 Integer getAtk();//每个武器都有攻击力 Float getCrit();//暴击率 } ? 武器类 public class Weapon implements IWeapon { private String name;//武器名称 private Integer atk;//攻击力 private Float crit;//暴击率 public Weapon(String name,Integer atk,Float crit) { this.name = name; this.atk = atk; this.crit = crit; } @Override public String getName() { return name; } @Override public Integer getAtk() { return atk; } @Override public Float getCrit() { return crit; } } ? main方法 public static void main(String[] args) { ArrayList<IWeapon> weapons = new ArrayList<>(); weapons.add(new Weapon("倚天剑",1200,0.6F)); weapons.add(new Weapon("打狗棒",850,0.7F)); weapons.add(new Weapon("血饮狂刀",900,0.56F)); weapons.add(new Weapon("绣花针",0.7F)); for(IWeapon weapon : weapons){ System.out.println(weapon.getName() + ",攻击力是" + weapon.getAtk() + ",暴击"+weapon.getCrit()); } } 输出: 倚天剑,攻击力是1200,暴击0.6 ? 东方不败的绣花针现在,我们来加一个需求,东方不败的武器绣花针,实际暴击应该在原基础上增加0.1,因为飞刀暗器之类的兵器总是让人防不胜防。 ? 面对这个需求,我们来研究一下解决办法 既然绣花针是特殊的武器,那么我们就在Weapon类增加一个参数,是否加暴,如果为true,那么就增加0.1的暴击,怎么传入呢?那就重载一个构造函数即可。 ? 修改后的Weapon类 public class Weapon implements IWeapon { private String name;//武器名称 private Integer atk;//攻击力 private Float crit;//暴击率 private boolean isAddCrit = false; public Weapon(String name,Float crit) { this.name = name; this.atk = atk; this.crit = crit; } public Weapon(String name,Float crit,boolean isAddCrit) { this.name = name; this.atk = atk; this.crit = crit; this.isAddCrit = isAddCrit; } @Override public String getName() { return name; } @Override public Integer getAtk() { return atk; } @Override public Float getCrit() { return isAddCrit ? crit + 0.1F : crit; } } ? 修改后的main方法 public static void main(String[] args) { ArrayList<IWeapon> weapons = new ArrayList<>(); weapons.add(new Weapon("倚天剑",0.7F,true)); for(IWeapon weapon : weapons){ System.out.println(weapon.getName() + ",暴击0.8 |