对扩展开放,对修改关闭
。在程序需要进行拓展的时候,不能去修改原有的代码
,实现一个热插拔
的效果。简言之,是为了使程序的扩展性好,易于维护和升级接口
和抽象类
例子:搜狗输入法的皮肤设计
分析:搜狗输入法的皮肤是输入法背景图片、窗口颜色和声音等元素的组合,用户可以根据自己的喜爱更换自己的输入法的皮肤,这些皮肤有共同的特点,可以为其定义一个抽象类(AbstractSkin),而每个具体的皮肤(DefaultSkin、MySkin)都是它的子类,这样用户就可以根据需要选择或者新增的主题,而不需要修改源代码,所以它是满足开闭原则
的
代码:
public abstract class AbstractSkin {
// 显示的方法
public abstract void display();
}
public class DefaultSkin extends AbstractSkin{
@Override
public void display() {
System.out.println("默认皮肤");
}
}
public class MySkin extends AbstractSkin{
@Override
public void display() {
System.out.println("我的皮肤");
}
}
DefaultSkin和MySkin是AbstractSkin子类
public class SougouInput {
private AbstractSkin abstractSkin;
public void setAbstractSkin(AbstractSkin abstractSkin) {
this.abstractSkin = abstractSkin;
}
public void display(){
abstractSkin.display();
}
}
public class Main {
public static void main(String[] args) {
SougouInput sougouInput = new SougouInput();
DefaultSkin defaultSkin = new DefaultSkin();
MySkin mySkin = new MySkin();
sougouInput.setAbstractSkin(mySkin);
sougouInput.display();
}
}
然后调用的时候,通过SougouInput的set方法,然后再调用display就可以实现多态了,如果想在加一个皮肤的话,只需要再实现AbstractSkin,然后调用即可(但是这里new还是操纵在我们的手里)
例子:正方形不是长方形
在数学领域中,正方形毫无疑问是长方形,它是一个长宽相等的长方形,所以我们开发的一个与几何图形相关的软件系统,就可以让正方形继承长方形
代码:
public class Rectangle {
private double length;
private double width;
public void setLength(double length) {
this.length = length;
}
public void setWidth(double width) {
this.width = width;
}
public double getLength() {
return length;
}
public double getWidth() {
return width;
}
}
基类长方形
public class Square extends Rectangle{
@Override
public void setWidth(double width) {
super.setWidth(width);
super.setLength(width);
}
@Override
public void setLength(double length) {
super.setLength(length);
super.setWidth(length);
}
}
正方形继承长方形
public class RectangleDemo {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setWidth(10);
rectangle.setLength(20);
resize(rectangle);
printWidthAndLength(rectangle);
System.out.println("=======");
Square square = new Square();
square.setLength(10);
resize(square);
printWidthAndLength(square);
}
public static void resize(Rectangle rectangle){
while(rectangle.getWidth() <= rectangle.getLength()){
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
public static void printWidthAndLength(Rectangle rectangle){
System.out.println("rectangle = " + rectangle.getLength());
System.out.println("width = " + rectangle.getWidth());
}
}
此时我们运行这个方法,======之前的会打印出来,后面的打印不出来,原因是因为Square中的setWidth重写了父类的setWidth,导致长和宽一直要保持相同的,它重写了父类的方法,导致了出错,也就是说正常性不是长方形,这个继承
不满足里式替换原则
。
代码:
public interface Quadrilateral {
double getLength();
double getWidth();
}
接口
public class Rectangle implements Quadrilateral{
private double length;
private double width;
@Override
public double getLength() {
return length;
}
@Override
public double getWidth() {
return width;
}
public void setLength(double length) {
this.length = length;
}
public void setWidth(double width) {
this.width = width;
}
}
长方形
public class Square implements Quadrilateral{
private double side;
@Override
public double getLength() {
return side;
}
@Override
public double getWidth() {
return side;
}
public void setSide(double side) {
this.side = side;
}
public double getSide() {
return side;
}
}
正方形
public class RectangleDemo {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setLength(10);
rectangle.setWidth(20);
resize(rectangle);
printWidthAndLength(rectangle);
System.out.println("========");
Square square = new Square();
square.setSide(10);
printWidthAndLength(square);
}
public static void resize(Rectangle rectangle){
while(rectangle.getWidth() <= rectangle.getLength()){
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
public static void printWidthAndLength(Quadrilateral quadrilateral){
System.out.println("rectangle = " + quadrilateral.getLength());
System.out.println("width = " + quadrilateral.getWidth());
}
}
这样的话,解决了刚才的问题(说实话,个人觉得接口比抽象类用的场景多,很少见过抽象类的)
高层模块应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象
,简单的来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
例:组装电脑
现要组装一台电脑,需要配件cpu,硬盘,内存条,只用这些配置都有了,计算机才能正常的运行。
public class XijieHardDisk {
public void save(String data){
System.out.println("使用希捷存储" + data);
}
public String get(){
System.out.println("使用希捷获取数据");
return "数据";
}
}
public class KingStoneMemory {
public void save(){
System.out.println("使用金士顿内存条");
}
}
public class IntelCpu {
public void run(){
System.out.println("使用Intel处理器");
}
}
public class Computer {
private XijieHardDisk xijieHardDisk;
private IntelCpu intelCpu;
private KingStoneMemory kingStoneMemory;
// get/set方法
public void run(){
System.out.println("运行计算机");
String s = xijieHardDisk.get();
System.out.println("从硬盘上获取的数据是" + s);
intelCpu.run();
kingStoneMemory.save();
}
}
public class ComputerDemo {
public static void main(String[] args) {
Computer computer = new Computer();
XijieHardDisk xijieHardDisk = new XijieHardDisk();
IntelCpu intelCpu = new IntelCpu();
KingStoneMemory kingStoneMemory = new KingStoneMemory();
computer.setIntelCpu(intelCpu);
computer.setKingStoneMemory(kingStoneMemory);
computer.setXijieHardDisk(xijieHardDisk);
computer.run();
}
}
从上面看我们已经组装好了一台电脑,但是这台电脑似乎有些不太理想,硬盘、内存、cpu都是写死的了,我们应该设计成想装什么配件就装什么配件,所以要根据依赖倒转原则进行改进:
public interface Memory {
public void save();
}
public interface HardDisk {
public void save(String data);
public String get();
}
public interface Cpu {
public void run();
}
这是三个接口,然后XijieHarddDisk、IntelCpu、KingStonMemory去继承他们
public class Computer {
private HardDisk disk;
private Cpu cpu;
private Memory memory;
// get/set方法
public void run(){
System.out.println("运行计算机");
String s = disk.get();
System.out.println("从硬盘上获取的数据是" + s);
cpu.run();
memory.save();
}
}
使用接口去作为Computer的属性
public class ComputerDemo {
public static void main(String[] args) {
Computer computer = new Computer();
computer.setCpu(new IntelCpu());
computer.setDisk(new XijieHardDisk());
computer.setMemory(new KingStoneMemory());
computer.run();
}
}
最后使用的时候,通过set实现那些抽象的具体类,也就是你想装什么配置就放什么,这样就很合理喽!
客户端不应该被迫依赖于它不使用的方法,一个类对另一个的依赖应该建立在最小的接口上
例:我们需要创建一个A品牌的安全门,该安全门具有防火、防水、防盗的功能,可以将防火、防水、防盗功能提取成为一个接口
上面的设计我们发现了它存在的问题,如果我们需要再创建一个B品牌的安全门,该安全门只具有防盗、防水的功能,如果实现SafetyDoor接口就违背了接口隔离原则,下面是修改优化
将防盗门的功能都抽取成最小接口,哪个防盗门要实现哪些就实现哪些。
迪米特法则又叫最小知识原则。只和你的直接朋友交谈,不跟陌生人说话,含义就是如果两个软件实体无须直接通信,那么就不应该发生直接的相互调用,可以通过第三方转发该调用,目的是为了降低类之间的耦合性,提高模块的相对独立性。
迪米特法则中的朋友是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合和组合关系,可以直接访问这些对象的方法。
例:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。
public class Star {
private String name;
public Star(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Fans {
private String name;
public Fans(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Company {
private String name;
public Company(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Agent {
private Star star;
private Fans fans;
private Company company;
public void setStar(Star star) {
this.star = star;
}
public void setCompany(Company company) {
this.company = company;
}
public void setFans(Fans fans) {
this.fans = fans;
}
public void meeting(){
System.out.println(fans.getName() + "与明星" + star.getName() + "见面了");
}
public void business(){
System.out.println(company.getName() + "与明星" + star.getName() + "谈业务");
}
}
public class Demo {
public static void main(String[] args) {
Agent agent = new Agent();
agent.setFans(new Fans("zzz"));
agent.setStar(new Star("lzy"));
agent.setCompany(new Company("iii"));
agent.meeting();
agent.business();
}
}
从类图中我们可以看出,粉丝、公司和明星如果要进行互动的话,要通过经纪人去调用,也就是只能通过经纪人这个朋友去做调用中介,符合迪米特法则。