对于Java中常用的设计模式, 规定了其设计原则,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,我们要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本
1 开闭原则
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果.
因为抽象灵活性好, 我们以接口和抽象类实现案例效果.
1 案例
以搜狗输入法的皮肤替换为例, 搜狗输入法有默认的皮肤,也能用户自定义皮肤.首先需要定义一个皮肤抽象类,让二者继承.
1 抽象皮肤类
public abstract class AbstractSkin {
public abstract void display();
}
2 默认皮肤类
public class DefaultSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("默认皮肤");
}
}
3 自定义皮肤类
public class CustomSkin extends AbstractSkin {
@Override
public void display() {
System.out.println("自定义皮肤");
}
}
4 搜狗拼音类
public class SougouInput {
private AbstractSkin skin;
public void setSkin(AbstractSkin skin) {
this.skin = skin;
}
public void display() {
skin.display();
}
}
5 测试类
public class Client {
public static void main(String[] args) {
SougouInput sougouInput = new SougouInput();
CustomSkin skin = new CustomSkin();
sougouInput.setSkin(skin);
sougouInput.display();
}
}
6 结果
// 第一次 默认皮肤
// 第二次 自定义皮肤
通过接口和抽象类,实现了搜狗拼音的皮肤切换,当后续出现其他皮肤类型,只需要创建一个实现类即可
2 里氏替换原则
里氏代换原则:任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能.
1 案例
以正方形和长方形为例, 因为正方形属于特殊的长方形. 所以首先创建一个长方形类,再创建一个正方形类继承长方形类.
1 长方形类
public class Rectangle {
private double length;
private double width;
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
}
2 正方形类
public class Square extends Rectangle {
@Override
public void setLength(double length) {
super.setLength(length);
super.setWidth(length);
}
@Override
public void setWidth(double width) {
super.setWidth(width);
super.setLength(width);
}
}
3 测试类
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setLength(20);
rectangle.setWidth(10);
printLengthAndWidth(rectangle);
System.out.println("-------------");
Square square = new Square();
square.setLength(10);
resize(square);
printLengthAndWidth(square);
}
public static void resize(Rectangle rectangle) {
while (rectangle.getLength() <= rectangle.getWidth()) {
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
public static void printLengthAndWidth(Rectangle rectangle) {
System.out.println(rectangle.getLength());
System.out.println(rectangle.getWidth());
}
}
4 结果
20.0
10.0
-------------
程序一直再循环运行 正方形的长和宽一直再增加...
2 案列改造
1 创建四方形接口
public interface Quadrilateral {
double getLength();
double getWidth();
}
2 创建长方形类
public class Rectangle implements Quadrilateral {
private double length;
private double width;
@Override
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
@Override
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
}
3 正方形类
public class Square implements Quadrilateral {
private double side;
public void setSide(double side) {
this.side = side;
}
public void setLength(double length) {
side = length;
}
@Override
public double getLength() {
return side;
}
@Override
public double getWidth() {
return side;
}
4 测试类
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setLength(20);
rectangle.setWidth(10);
printLengthAndWidth(rectangle);
System.out.println("-------------");
Square square = new Square();
square.setLength(10);
printLengthAndWidth(square);
}
public static void resize(Rectangle rectangle) {
while (rectangle.getLength() <= rectangle.getWidth()) {
rectangle.setWidth(rectangle.getWidth() + 1);
}
}
public static void printLengthAndWidth(Quadrilateral quadrilateral) {
System.out.println(quadrilateral.getLength());
System.out.println(quadrilateral.getWidth());
}
}
5 结果
20.0
10.0
-------------
10.0
10.0
3 依赖倒转原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象.
1 案例
以电脑类为例. 一个电脑需要基本的cpu,硬盘,内存条.
1 电脑类
public class Computer {
private XiJieHardDisk hardDisk;
private IntelCpu cpu;
private KingstonMemory memory;
public IntelCpu getCpu() {
return cpu;
}
public void setCpu(IntelCpu cpu) {
this.cpu = cpu;
}
public KingstonMemory getMemory() {
return memory;
}
public void setMemory(KingstonMemory memory) {
this.memory = memory;
}
public XiJieHardDisk getHardDisk() {
return hardDisk;
}
public void setHardDisk(XiJieHardDisk hardDisk) {
this.hardDisk = hardDisk;
}
public void run() {
System.out.println("计算机工作");
cpu.run();
memory.save();
String data = hardDisk.get();
System.out.println("从硬盘中获取的数据为:" + data);
}
}
2 希捷硬盘类
public class XiJieHardDisk {
public void save(String data) {
System.out.println("使用希捷硬盘存储数据" + data);
}
public String get() {
System.out.println("使用希捷希捷硬盘取数据");
return "数据";
}
}
3 Intel处理器类
public class IntelCpu {
public void run() {
System.out.println("使用Intel处理器");
}
}
4 金士顿内存条类
public class KingstonMemory {
public void save() {
System.out.println("使用金士顿作为内存条");
}
}
5 测试类
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
computer.setHardDisk(new XiJieHardDisk());
computer.setCpu(new IntelCpu());
computer.setMemory(new KingstonMemory());
computer.run();
}
}
6 运行结果
计算机工作
使用Intel处理器
使用金士顿作为内存条
使用希捷希捷硬盘取数据
从硬盘中获取的数据为:数据
上述电脑,相关配件已经固定,如果想要换成AMD的cpu或其他品牌的内存条等就不行了.
2 案例改造
1 电脑类
public class Computer {
private HardDisk hardDisk;
private Cpu cpu;
private Memory memory;
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
public Memory getMemory() {
return memory;
}
public void setMemory(Memory memory) {
this.memory = memory;
}
public HardDisk getHardDisk() {
return hardDisk;
}
public void setHardDisk(HardDisk hardDisk) {
this.hardDisk = hardDisk;
}
public void run() {
System.out.println("计算机工作");
cpu.run();
memory.save();
String data = hardDisk.get();
System.out.println("从硬盘中获取的数据为:" + data);
}
}
2 希捷硬盘类
public class XiJieHardDisk implements HardDisk {
public void save(String data) {
System.out.println("使用希捷硬盘存储数据" + data);
}
public String get() {
System.out.println("使用希捷希捷硬盘取数据");
return "数据";
}
}
3 AMD处理器类
public class IntelCpu implements Cpu {
public void run() {
System.out.println("使用AMD处理器");
}
}
4 金士顿内存条
public class KingstonMemory implements Memory {
public void save() {
System.out.println("使用金士顿作为内存条");
}
}
5 测试类
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
computer.setHardDisk(new XiJieHardDisk());
computer.setCpu(new AMDCpu());
computer.setMemory(new KingstonMemory());
computer.run();
}
}
6 结果
计算机工作
使用AMD处理器
使用金士顿作为内存条
使用希捷希捷硬盘取数据
从硬盘中获取的数据为:数据
根据依赖倒转原则 , 我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类即可.
4 接口隔离原则
客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上.
1 案例
以安全门为例,安全门有防水防盗的功能, 小牛牌安全门具备上述所有功能
1 安全门接口
public interface SafeDoor {
void antiTheft();
void fireproof();
void waterproof();
}
2 小牛牌
public class XiaoNiuDoor implements SafeDoor {
@Override
public void antiTheft() {
}
@Override
public void fireproof() {
}
@Override
public void fireproof() {
}
}
此时,出现了一个小狗牌,他只能防水防盗,没有防火功能,就不好处理
2 案例改造
1 防盗接口
public interface AntiTheft {
void antiTheft();
}
2 防火接口
public interface Fireproof {
void fireproof();
}
3 防水接口
public interface Waterproof {
void waterproof();
}
4 小狗牌
public class XiaoGouDoor implements AntiTheft, Waterproof{
@Override
public void antiTheft() {
}
@Override
public void fireproof() {
}
}
将所有功能分开到不同的接口中,类需要那种功能就实现那种接口.
5 迪米特法则
迪米特法则又叫最少知识原则. 如果两个实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用.
1 案例
以 明星,经纪人,粉丝,广告公司之间为例, 明星和粉丝见面由经纪人安排,明星和广告公司合作由经纪人安排,所以通过经纪人将三者关联.
1 明星类
public class Star {
private String name;
public Star(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
2 粉丝类
public class Fans {
private String name;
public Fans(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
3 广告公司类
public class Company {
private String name;
public Company(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
4 经纪人类
public class Agent {
private Star star;
private Fans fans;
private Company company;
public void setStar(Star star) {
this.star = star;
}
public void setFans(Fans fans) {
this.fans = fans;
}
public void setCompany(Company company) {
this.company = company;
}
public void meeting() {
System.out.println(fans.getName() + "与明星" + star.getName() + "见面了。");
}
public void business() {
System.out.println(company.getName() + "与明星" + star.getName() + "商谈业务。");
}
}
通过经纪人, 将明星和粉丝, 公告公司关联起来,实现最少关联.
6 合成复用原则
合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现.
1 案例
以汽车分类划分, 车按照动力分,汽油车,电动车; 按照颜色分,红车,白车.
1 按照继承关系来划分
1 汽车类
public class Car {
public void move(){
}
}
2 油车
public class PetroCar extends Car{
@Override
public void move() {
super.move();
}
}
3 红色油车
public class RedPetroCar extends PetroCar{
@Override
public void move() {
super.move();
}
}
4 白色油车
public class WhitePetroCar extends PetroCar{
@Override
public void move() {
super.move();
}
}
5 电车
public class EleCar extends Car {
@Override
public void move() {
super.move();
}
}
6 红色电车
public class RedEleCar extends Car {
@Override
public void move() {
super.move();
}
}
7 白色电车
public class WhiteEleCar extends EleCar {
@Override
public void move() {
super.move();
}
}
如果出现新的氢能源车 ,又得新增三个类.
2 按照聚合复用关系来划分
把颜色抽象成接口,聚合复用到汽车类中.
1 颜色接口
public interface Color {
}
2 汽车类
public class Car {
private Color color;
public void move(){
}
}
然后油车, 电车可以传入不同的颜色,构造出不同颜色的车型. 如果新增氢能源车,也只需要新增一个类即可.
所以, 在类机构设计时尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
|