设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编写真正工程化,设计模式是软件工程的基石,如同大厦的设计图一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
一、设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说:对扩展开放,对修改关闭。在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、依赖倒转原则(Dependence Inversion Principle)
依赖倒转原则就是说:针对接口编程,依赖于抽象而不依赖于具体。它是开闭原则的基础。
3、里氏代换原则(Liskov Substitution Principle)
里氏代换原则就是说:任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
4、接口隔离原则(Interface Segregation Principle)
接口隔离原则就是说:使用多个隔离的接口,比使用单个接口要好。其实也就是降低类之间的耦合度的意思。
5、合成复用原则(Composite Reuse Principle)
合成复用原则就是说:尽量使用合成/聚合的方式,而不是使用继承。
6、迪米特原则(最少知道原则)(Demeter Principle)
迪米特原则就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
二、设计模式的分类与应用
总体来说设计模式分为三大类(23种):
结构型模式(7种):适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
1、适配器模式:将原有类或接口转换成客户端期望的类或接口,目的是消除由于接口不匹配所造成的类兼容性问题。分3种:
1>类适配器模式:适配器类实现了目标接口,并且继承了被适配的类。适配器类与被适配的类是一种继承关系。此模式由3部分组成:
①目标接口:即客户期望成的接口。
public interface Animal{
public void say();
}
目标接口的实现:
public class OrdinaryAnimal implements Animal{
public void say() {
System.out.println("普通动物的叫声");
}
}
②被适配的类:即通过目标接口也能访问的类
public class People {
public void speak(){
System.out.println("人类的语言");
}
}
③适配器类:
public class PeopleAdapter extends People implements Animal {
public void say(){
super.speak();
}
}
主入口:
public class Test {
public static void main(String[] args) {
Animal animal1 = new OrdinaryAnimal();
animal1.say();
Animal animal2 = new PeopleAdapter();
animal2.say();
}
}
2>对象适配器模式:适配器类实现了目标接口,并且将被适配的类的作为适配器类的成员变量。适配器类与被适配的类是一种包含关系。此模式由3部分组成:
①目标接口:即客户期望成的接口。
public interface Animal{
public void say();
}
目标接口的实现:
public class OrdinaryAnimal implements Animal{
public void say() {
System.out.println("普通动物的叫声");
}
}
②被适配的类:即通过目标接口也能访问的类
public class People {
public void speak(){
System.out.println("人类的语言");
}
}
③适配器类:
public class PeopleAdapter implements Animal {
private People people;
public PeopleAdapter(People people){
this.people=people;
}
public void say() {
people.speak();
}
}
主入口:
public class Test {
public static void main(String[] args) {
Animal animal1 = new OrdinaryAnimal();
animal1.say();
People people = new People();
Animal animal2 = new PeopleAdapter(people);
animal2.say();
}
}
双向适配器:如果目标接口需要被适配的类适配,而被适配的类也需要目标接口适配,那么就需要将目标接口和被适配的类都作为适配器类的成员变量。
3>缺省适配器模式(单接口适配器模式):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用接口中的所有方法的情况。
①原接口:
public interface Animal{
public void say();
public void run();
public void fly();
}
②抽象类:
public abstract class AnimalClass implements Animal {
public void say(){
}
public void run(){
}
public void fly(){
}
}
③继承类1:
public class People extends AnimalClass{
public void say(){
System.out.println("人类的语言");
}
public void run(){
System.out.println("使用双腿奔跑");
}
}
继承类2:
public class Bird extends AnimalClass {
public void say(){
System.out.println("鸟的语言");
}
public void fly(){
System.out.println("使用翅膀飞翔");
}
}
主入口:
public class Test {
public static void main(String[] args) {
Animal people = new People();
people.say();
people.run();
Animal bird = new Bird();
bird.say();
bird.fly();
}
}
2、装饰模式:动态地给一个对象增加一些额外的功能,就增加对象功能来说,装饰模式比添加继承子类实现更为灵活。此模式由4部分组成:
①抽象构件接口:被装饰类和装饰类都会实现此接口,实现统一管理。
public interface Animal {
public void act();
}
②被装饰类:原有的功能,需要在此基础上新增功能。
public class People implements Animal{
public void act(){
System.out.println("我可以用双脚走路");
}
}
③抽象装饰类:将构件接口作为本类的成员变量,通过此变量可以调用装饰之前的功能,而实际新增功能是在其子类中实现的。
public class SuperMan implements Animal{
private Animal animal;
public SuperMan(Animal animal){
this.animal=animal;
}
public void act() {
animal.act();
}
}
④具体装饰类:
public class IronMan extends SuperMan {
public IronMan(Animal animal){
super(animal);
}
public void act() {
super.act();
this.addAct();
}
public void addAct(){
System.out.println("我还可以在天空飞");
}
}
主入口:
public class Test {
public static void main(String[] args) {
Animal animal1 = new People();
Animal animal2 = new IronMan(animal1);
animal2.act();
}
}
透明装饰模式:要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。透明装饰模式可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象。
半透明装饰模式:要求客户端程序必须使用具体的装饰类型(具体装饰类)定义被装饰后的对象,所以不能对一个已装饰过的对象进行多次装饰。
3、代理模式:使用代理对象来替代实际对象,代理对象和实际对象要实现同一个接口。此模式由3部分组成:
①接口:声明了代理对象和实际对象的共同接口。
public interface Human{
public void act();
}
②实际对象:包含实际的业务。
public class RealPeople implements Human{
public void act(){
System.out.println("我是真正的幕后老大");
}
}
③代理对象:包含对实际对象的引用,从而在任何时候都可以操作实际对象。
public class proxyPeople implements Human {
private Human human;
public proxyPeople(){
this.human=new RealPeople();
}
public void act() {
human.act();
}
}
主入口:
public class Test {
public static void main(String[] args) {
Human people = new ProxyPeople();
people.act();
}
}
4、外观模式:对多个对象的访问都是通过同一个对象统一管理统一访问。此模式由2部分组成:
①子系统角色1:
public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
子系统角色2:
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
子系统角色3:
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
②外观角色:
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
主入口:
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}
5、桥接模式:当某个类同时存在两个独立变化的维度时,需要将这两个维度分离开,使两者可以独立扩展。此模式由4部分组成:
①维度1的抽象类:将维度2作为一个成员变量。
public abstract class Pen {
protected Draw draw;
Pen(Draw draw){
this.draw=draw;
}
public abstract void draw();
}
②维度1的子类1:
public class Pen1 extends Pen {
Pen1(Draw draw){
super(draw);
}
public void draw() {
System.out.println("第1种笔正在画");
this.draw.drawCircle();
}
}
维度1的子类2:
public class Pen2 extends Pen {
Pen2(Draw draw){
super(draw);
}
public void draw() {
System.out.println("第2种笔正在画");
this.draw.drawSquare();
}
}
维度1的子类3:
public class Pen3 extends Pen {
Pen3(Draw draw){
super(draw);
}
public void draw() {
System.out.println("第3种笔正在画");
this.draw.drawTriangle();
}
}
③维度2的接口:
public interface Draw {
public void drawCircle();
public void drawSquare();
public void drawTriangle();
}
④维度2的实现1:
public class Draw1 implements Draw {
public void drawCircle() {
System.out.println("画圆的第1种画法");
}
public void drawSquare() {
System.out.println("画矩形的第1种画法");
}
public void drawTriangle() {
System.out.println("画三角形的第1种画法");
}
}
维度2的实现2:
public class Draw2 implements Draw {
public void drawCircle() {
System.out.println("画圆的第2种画法");
}
public void drawSquare() {
System.out.println("画矩形的第2种画法");
}
public void drawTriangle() {
System.out.println("画三角形的第2种画法");
}
}
维度2的实现3:
public class Draw3 implements Draw {
public void drawCircle() {
System.out.println("画圆的第3种画法");
}
public void drawSquare() {
System.out.println("画矩形的第3种画法");
}
public void drawTriangle() {
System.out.println("画三角形的第3种画法");
}
}
主入口:
public class Test {
public static void main(String[] args) {
Draw draw1 = new Draw1();
Draw draw2 = new Draw2();
Draw draw3 = new Draw3();
Pen pen1 = new Pen1(draw1);
Pen pen2 = new Pen2(draw2);
Pen pen3 = new Pen3(draw3);
pen1.draw();
pen2.draw();
pen3.draw();
}
}
6、组合模式:也叫部分-整体模式,即组合对象的结构和单个对象的结构是相同的。此时本类的对象作为本类中的一个成员变量,常用于树结构或文件目录中的节点。
TreeNode:
import java.util.Vector;
public class TreeNode {
private TreeNode parentNode;
private String nodeName;
private Vector<TreeNode> childNodes=new Vector<TreeNode>();
TreeNode(String name){
this.nodeName = name;
}
public TreeNode getParentNode() {
return parentNode;
}
public void setParentNode(TreeNode parentNode) {
this.parentNode = parentNode;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public Vector<TreeNode> getChildNodes() {
return childNodes;
}
public void setChildNodes(Vector<TreeNode> childNodes) {
this.childNodes = childNodes;
}
public void addNode(TreeNode node){
this.childNodes.add(node);
}
public void delNode(int num){
this.childNodes.remove(num);
}
public String getChildNodeNames(TreeNode node){
String nodes = "";
for(TreeNode tmp:node.childNodes){
nodes+=tmp.getNodeName()+","+tmp.getChildNodeNames(tmp);
}
return nodes;
}
}
Tree:
public class Tree {
static TreeNode root = new TreeNode("root");
public static void main(String[] args) {
TreeNode node1 = new TreeNode("node1");
TreeNode node11 = new TreeNode("node11");
TreeNode node12 = new TreeNode("node12");
TreeNode node2 = new TreeNode("node2");
TreeNode node21 = new TreeNode("node21");
TreeNode node22 = new TreeNode("node22");
TreeNode node221 = new TreeNode("node221");
root.addNode(node1);
root.addNode(node2);
node1.addNode(node11);
node1.addNode(node12);
node2.addNode(node21);
node2.addNode(node22);
node22.addNode(node221);
System.out.println(root.getChildNodeNames(root));
}
}
7、享元模式:主要目的是实现对象的共享,即共享池,当系统中有相应的对象时就不再创建该对象,因此可以减少内存的开销,通常与工厂模式一起使用。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
public class ConnectionPool{
private Vector<Connection> pool;
private int poolSize = 10;
Connection conn = null;
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
private ConnectionPool() {
pool = new Vector<Connection>(poolSize);
for (int i = 0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//释放回到连接池
public synchronized void release() {
pool.add(conn);
}
//得到连接池中的一个数据库连接
public synchronized Connection getConnection() {
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
} else {
return null;
}
}
}