做到OOD的代码需要满足的条件:
- 面向对象
- 复用
- 能以最小的代价满足变化
- 不用改变现有代码满足扩展
面向对象设计原则
开闭原则OCP
Open-Closed Principle
开闭原则是指:软件组成实体应该是对扩展开放的,但是对修改关闭的。
实现开闭原则关键在于——抽象化:
可以给系统定义一个不在更改的抽象设计,允许无尽的行为在实现层被实现。抽象层预测所有可能扩展。
一个软件系统的所有模块不可能都满足OCP,但是应该努力最小化这些不满足OCP的模块数量。
----------销售打折问题:
//零件类
class Part {
protected double basePrice;
private PricePolicy pricePolicy;
public void setPricePolicy(PricePolicy policy) {
pricePolicy = policy;
}
public void setPrice(double price) {
basePrice = price;
}
public double getPrice() {
if (pricePolicy == null)
return basePrice;
else
return pricePolicy.getPrice(basePrice);
}
}
//内存、磁盘继承零件
class Memory extends Part{
}
class Disk extends Part{
}
//价格政策——原价
class PricePolicy{
public double getPrice(double basePrice){
return basePrice;
}
}
//促销——原价基础上打折
class Sale extends PricePolicy{
private double discount;
public void setDiscount(double discount){
this.discount = discount;
}
public void double getPrice(double basePrice){
return basePrice * discount;
}
}
//测试类
public class TestOCP {
public static void main(String[] args) {
Part p1 = new Memory();
p1.setPrice(599);
Part p2 = new Disk();
p2.setPrice(499);
Sale sale = new Sale();
sale.setDiscount(0.75);
//磁盘打折
p2.setPricePolicy(sale);
Part[] com = {
p1, p2 };
System.out.println(totalprice(com));
}
----------画图形问题:
//编写一个shape抽象类
//这个类仅有一个抽象方法draw()
//所有形状都从这个类派生
//当绘制一种新的形状,只需要增加一个新的shape类的派生类。而DrawAllShapes 函数并不需要改变。
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
void draw() {
System.out.println("I am drawing a circle!");
}
}
class Square extends Shape {
void draw() {
System.out.println("I am drawing a square!");
}
}
public class TestOCP2 {
public static void main(String[] args) {
List<Shape> shapeList=new ArrayList<Shape>();
Circle c=new Circle();
Square s=new Square();
shapeList.add(c);
shapeList.add(s);
DrawAllShape drawAllShape=new DrawAllShape();
drawAllShape.drawAllShapes(shapeList);
}
}
class DrawAllShape {
void drawAllShapes(List<Shape> shapeList) {
for (int i=0;i<shapeList.size();i++)
// 通过for循环取出list中每个//shape
{
Shape shape= (Shape)shapeList.get(i);
shape.draw();}
}
}
里氏代换原则LSP
Liskov Substitution Principle
通俗的定义:所有引用基类的地方必须能透明地使用其子类的对象。
更通俗的定义:子类可以扩展父类的功能,但不能改变父类原有的功能。
一个软件如果使用的是一个父类的话,如果把该父类换成子类,它不能察觉出父类对象和子类对象的区别。
凡是父类适用的地方子类也适用
继承只有满足里氏代换原则才是合理的。
如果两个具体的类A,B之间的关系违反了LSP的设计,(假设是从B到A的继承关系),那么根据具体的情况可以在下面的两种重构方案中选择一种。
- 创建一个新的抽象类C,作为两个具体类的超类,将A,B的共同行为移动到C中来解决问题。
- 从B到A的继承关系改为委派关系。
依赖倒置原则DIP
dependency inversion principle
依赖倒置原则指——
- 高层模块不应该依赖于低层模块,二者应该依赖其抽象。
- 抽象不应该依赖于细节,细节应该依赖抽象。
这里的抽象一般指 抽象类与接口,两者都是不能够实例化的, 细节指实现接口或继承抽象类所产生的类,可以通过new实例化。
依赖倒置原则的本质就是通过抽象使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合。
这个原则也是OO设计原则中最难以实现的了,如果没有实现这个原则,那么也就意味着开闭原则也无法实现。
常见应用:AGP插槽;驾照;电源插座。
设计模式中最能体现DIP原则的是——抽象工厂模式,工厂和产品都是抽象的。
如何遵循依赖倒置原则:
- 通过构造函数传递依赖对象(参数是抽象类或接口)
- 通过setter方法传递依赖对象(参数为抽象类或接口)
- 接口声明实现依赖对象(参数为抽象类或接口)
//通过构造函数传递依赖对象
public interface IDriver {
public void drive();
}
public class Driver implements IDriver{
private ICar car;
//构造函数注入
public Driver(ICar _car){
this.car = _car;
}
public void drive(){
this.car.run();
}
}
//通过setter方法传递依赖对象
public interface IDriver {
//车辆型号
public void setCar(ICar car);
public void drive();
}
public class Driver implements IDriver{
private ICar car;
//setter依赖注入
public void setCar(ICar car){
this.car = car;
}
public void drive(){
this.car.run();
}
}
public interface ICar {
public void run();
}
public class Benz implements ICar{
public void run(){
System.out.println("奔驰汽车开始运行...");
}
}
public class Client {
public static void main(..) {
IDriver zhangSan = new Driver();
ICar benz = new Benz();
zhangsan.setCar(benz);}
}
//接口声明实现依赖对象
public interface ICar {
public void run();
}
public class Benz implements ICar{
public void run(){
System.out.println("奔驰汽车开始运行...");
}
}
public interface IDriver {
public void drive(ICar car);
}
public class Driver imp