面向对象设计的三大特点与五大基本原则
一、面向对象设计的三个基本要素
面向对象的三个基本特征是:封装、继承、多态。
1. 封装性
封装是一种信息隐蔽技术,他体现于类的说明,是都西昂重要的特性。
封装使得数据和操作数据的方法封装成一个整体,想成为独立性很强的模块,使得用户只能看到对象的外部特征,内部是看不到的。
例:属性私有,get/set方法,有参构造 ,方法
2.继承性
继承是一种由已有类创建子类的机制,利用继承,可以先创建一个共有属性的一般类,根据这个类再创建具有特殊属性的子类,被继承的类成为父类,当然子类也可以成为父类来继续向下扩展。
例:子承父类
3.多态
同一个信息被不同的对象接收到时可能产生不同的行为,这就是多态性。有继承(接口)有重写,父类引用指向子类对象,就会产生多态。多态可以改善程序的组织架构,提高程序的可扩展性。
例:父类指向子类对象,子类重写父类方法
二、面向对象设计的五个基本设计原则
面向对象设计的五个基本设计原则是:单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口隔离原则
1.单一职责原则
就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因。
也可以理解为引用变化的原因,当你发现有两个变化会要求我们修改这个类,那么你就要考虑撤分这个类了。因为职责是变化的一个轴线,当需求变化时,该变化会反映类的职责的变化。
“就像一个人身兼数职,而这些事情相互关联不大,甚至有冲突,那他就无法很好的解决这些职责,应该分到不同的人身上去做才对。”
interface Modem//猫 { public void dial(string pno);//拨号 public void hangup();//挂断 public void send(char c);//发送 public void recv();//接受 } //应改为 interface DataChannel//数据通道 { public void send(char c); public void recv(); } interface Connection//链接 { public void dial(string pno); public void hangup(); }
优点:消除耦合,减小因需求变化引起代码僵化
2·开放封闭原则
其核心思想是:软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭。开放封闭原则主要体现在两个方面 1、对扩展开放,意味着有新的需求或者变化时,可以对现有代码进行扩展,以适应新的情况。 2、对修改封闭,意味着一旦设计完成,就可以独立完成其工作,而不要对其进行任何尝试的修改。
public interface Bark { public void bark(); } public class Awaw implements Bark{ @Override public void bark() { System.out.println("嗷嗷~~"); } } public class Zhazha implements Bark{ @Override public void bark() { System.out.println("喳喳~~"); } } public abstract class Bird { //声明属性 private Bark bark;//接口的对象 //设置set属性 public void setBark(Bark bark) { this.bark = bark; } /** * 鸟叫的方法 */ public void birdBark(){ bark.bark(); } } public class Test { public static void main(String[] args) { //测试 //声明鸟的叫声 Bark awaw = new Awaw(); Bark zhazha = new Zhazha(); //bird对象 Bird rocketBird = new RocketBird(); rocketBird.setBark(awaw); Bird splitBird = new SplitBird(); splitBird.setBark(zhazha); } }
优点:
1、降低程序各部分之间的耦合性,使程序模块互换成为可能; 2、使软件各部分便于单元测试,通过编制与接口一致的模拟类(Mock),可以很容易地实现软件各部分的单元测试; 3、利于实现软件的模块的呼唤,软件升级时可以只部署发生变化的部分,而不会影响其它部分;
3·里氏替换原则
核心思想:子类必须能够替换其父类。这一思想体现为对继承机制的约束规范,只有子类能够替换父类时才能保证系统在运行期内识别子类,这是保证继承复用的基础。在父类和子类的具体行为中,必须严格把握继承层次中的关系和特征,将父类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换父类,但是父类不一定能替换子类。
对于这个原则,通俗一些的理解就是,父类的方法都要在子类中实现或者重写。
public abstract class Bird { /** * 飞的方法 */ void fly(){ System.out.println("弹跳-飞"); } /** * 叫的方法 */ void call(){ System.out.println("嗷~!"); } /** * 攻击的抽象方法 */ abstract void attack(); } public class RocketBird extends Bird { @Override void attack() { System.out.println("加速冲刺"); } } public class SplitBird extends Bird{ @Override void attack() { System.out.println("分裂攻击"); } } public class Test { public static void main(String[] args) { Bird splitBird = new SplitBird(); Bird rocketBird = new RocketBird(); splitBird.attack(); rocketBird.attack(); } }
优点:
1、保证系统或子系统有良好的扩展性。只有子类能够完全替换父类,才能保证系统或子系统在运行期内识别子类就可以了,因而使得系统或子系统有了良好的扩展性。 2、实现运行期内绑定,即保证了面向对象多态性的顺利进行。这节省了大量的代码重复或冗余。避免了类似instanceof这样的语句,或者getClass()这样的语句,这些语句是面向对象所忌讳的。 3、有利于实现契约式编程。契约式编程有利于系统的分析和设计,指我们在分析和设计的时候,定义好系统的接口,然后再编码的时候实现这些接口即可。在父类里定义好子类需要实现的功能,而子类只要实现这些功能即可。
4·依赖倒置原则
其核心思想是:依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
反面例子:
缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。
解决方法:
优点:更为通用、更为稳定。
优点:
使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。
5·接口隔离原则
其核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口。
“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”