21 常用的设计原则和设计模式

301 阅读7分钟

1 常用的设计原则

软件的开发流程是:需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级

开发中常用的设计原则有:

  • 开闭原则:对扩展开发对修改关闭,这样可以使程序的可扩展性更好,易于维护和升级
  • 里氏代换原则:任何基类可以出现的地方,子类一定可以出现,多使用多态的方式
  • 依赖转换原则:尽量多依赖于抽象类或者接口而不是具体实现类,对子类具有强制性和规范性
  • 接口隔离原则:尽量多使用小接口而不是大接口,避免接口污染,降低类之间的耦合度
  • 迪米特法则(最少知道原则):一个实体应当少与其他实体之间发生相互作用,使系统功能模块相对独立
  • 合成复用原则:尽量多使用合成/聚合的方式,而不是继承的方式

2 常用的设计模式

所谓设计模式就是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结,简单来说就是用于固定场合的固定套路。

设计模式可以分为:

  • 创建型模式:单例设计模式、工厂方法模式、抽象工厂模式等
  • 结构型模式:装饰器模式、代理模式等
  • 行为型模式:模版设计模式等

下面会详细介绍这些设计模式:

单例设计模式

单例设计模式分为懒汉式和饿汉式两种,懒汉式需要对多线程进行同步处理,所以一般开发中推荐采用饿汉式。

下面会实现饿汉式的单例设计模式,具体代码如下:

public class Singleton {
    private static Singleton sin=new Singleton();
    private Singleton(){}
    private static Singleton getInstance(){
        return sin;
    }

}

下面再实现懒汉式的单例设计模式,需要处理多线程同步问题,具体代码如下:

public class Singleton2 {
    private static Singleton2 sin=null;
    private Singleton2(){}
    public static Singleton2 getInstance() {
        if (null == sin) {
            synchronized (Singleton2.class) {
                if (null == sin) {
                    sin = new Singleton2();
                }
            }
        }
        return sin;
    }
}

普通工厂方法模式

普通工厂方法模式就是建立一个工厂,对实现类同一个接口的不同实现类进行实例的创建,普通工厂方法的类图结构如下:

1.PNG

根据上面的类图实现·普通工厂方法模式,具体代码如下:

Sender接口

package task21;

public class Test {
    public static void main(String[] args) {
        SenderFactory sf=new SenderFactory();
        Sender mail = sf.prodece("sms");
        mail.send();
    }
}

MailSender类

package task21;

public class MailSender implements Sender{
    @Override
    public void send() {
        System.out.println("正在发送邮件。。。。。");
    }
}

SmsSender类

package task21;

public class SmsSender implements Sender{
    @Override
    public void send() {
        System.out.println("正在发送短信。。。。");
    }
}

SenderFactory类

package task21;

public class SenderFactory {
    public Sender prodece(String s){
        Sender sender=null;
        if("mail".equals(s)){
            sender=new MailSender();
        }
        if("sms".equals(s)){
            sender=new SmsSender();
        }
        return sender;
    }

}

通过上面的代码可以看出普通工厂方法模式的缺点是当传递的字符串出错,就无法正确创建对象,并且引发空指针异常错误

多个工厂方法模式

多个工厂方法模式是对普通工厂方法设计模式缺点的改进,具体类图设计如下:

2.PNG

多个工厂方法设计模式与普通工厂方法设计模式唯一的不同是在SenderFactory类,多个工厂方法在这个类中有多个工厂方法,具体实现代码是:

package task21;

public class SenderFactory {
    public Sender produceMail(){
        return new MailSender();
    }
    public Sender produceSms(){
        return new SmsSender();
    }


}

多个工厂方法设计模式虽然解决了单个工厂方法设计模式传字符串容易出错的问题,但是没有解决每次创建对象时,都需要先创建工厂类的对象才能调用工厂类中的生产方法。

静态工厂方法模式

为了解决上面提到的多个工厂模式的缺点,引出了静态工厂方法模式,具体类图如下:

3.PNG

静态工厂方法模式与多个工厂方法模式不同点在于工厂类中生产方法添加static关键字,这样就不用每次创建工厂类的对象,而是通过类名直接调用工厂类的生产方法,具体代码如下:

package task21;

public class SenderFactory {
    public static Sender produceMail(){
        return new MailSender();
    }
    public static Sender produceSms(){
        return new SmsSender();
    }


}

通过上面几个工厂方法模式不难看出工厂模式是适合在大量的产品需要创建且具有共同的接口时,可以通过工厂方法模式进行创建,但是工厂方法模式也存在的问题是,类的创建依赖工厂类,当想要拓展程序生产新的产品,就需要对工厂类进行修改,这样就违背了开闭眼则。

抽象工厂模式

抽象工厂模式可以解决工厂模式上面所说的缺点,具体类图如下:

4.PNG

抽象工厂模式实现代码是:

Provider接口

package task21;

public interface Provider {
    public Sender produce();
}

MailSendFactory类

package task21;

public class MailSendFactory implements Provider{
    @Override
    public Sender produce() {
        return new MailSender();
    }
}

SmsSendFactory类

package task21;

public class SmsSendFactory implements Provider{
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

装饰器模式

装饰器模式就是给一个对象动态的增加一些新功能,要求装饰对象和被被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。类图结构如下:

5.PNG

装饰器模式实现代码:

Sourceable接口

package task21;

public interface Sourceable {
    public void method();
}

Source类

package task21;

public class Source implements Sourceable{
    @Override
    public void method() {
        System.out.println("素颜时最美");
    }
}

Decorator类

package task21;

public class Decorator implements Sourceable{
    private Sourceable source;

    public Decorator(Sourceable source) {
        this.source = source;
    }

    @Override
    public void method() {
        source.method();
        System.out.println("化妆后更美");

    }
}

装饰器模式的实际意义:

  • 可以实现一个类功能的扩展
  • 可以动态的增加功能,而且还能动态撤销(继承不能撤销)
  • 缺点:产生很多相似的对象,不易排错

代理模式

代理模式就是找一个代理类替换原对象进行一些操作,具体类图结构如下:

6.PNG

代理模式实现:

package task21;

public class Proxy implements Sourceable {
    private Source source;

    public Proxy(Source source) {
        this.source = source;
    }

    @Override
    public void method() {
        source.method();
        System.out.println("我和迭代器模式是不一样的");

    }
}

代理模式实际意义:

  • 当需要对原有方法进行改进时,可以采用一个代理类调用原有方法,并且对产生的结果进行控制,这种方式就是代理模式
  • 使用代理模式,可以将功能划分的更加清晰,有助于后期维护

代理模式和装饰器模式的比较:

  • 装饰器模式通常的做法是将原始对象作为一个参数传给装饰者的构造器,而代理模式通常在一个代理类中创建一个被代理的对象
  • 装饰器模式主要关注在一个对象上动态的添加方法声明,而代理模式关注于控制对对象的访问

模版方法模式

模版方法模式主要指一个抽象类中封装了一个固定流程,流程中的具体步骤可以由不同的子类进行不同的实现,通过抽象类让固定的流程产生不同的结果,模版方法模式的类图结构是:

7.PNG

模版方法模式实现:

AbstarctCalculator抽象方法

package task21;

public abstract class  AbstarctCalculator {
    public int splitExpression(String exp,String op){
        String[] split = exp.split(op);
        return calculate(Integer.parseInt(split[0]),Integer.parseInt(split[1]));


    }
    public abstract int calculate(int a,int b);

}

Plus类

package task21;

public class Plus extends AbstarctCalculator{
    @Override
    public int calculate(int a, int b) {
        return a+b;
    }
}

Minus类

package task21;

public class Minus extends AbstarctCalculator{
    @Override
    public int calculate(int a, int b) {
        return a-b;
    }
}

实际意义:

  • 将多个子类共有且逻辑基本相同的内容提取出来实现代码复用
  • 不同的子类实现不同的效果形成多态,有助于后期维护