Java设计模式总结篇:七大原则以及23种设计模式简单介绍

2022-09-21 13:38:28

一、前言

Java设计模式在平时的开发中起到了至关重要的作用。设计模式的主要目的是:
1、降低耦合度,使得类的修改不至于“牵一发而动全身”。
2、提高可扩展性,新增功能对原有的代码没什么影响
3、提高可复用性,降低过多的使用类时,导致类爆炸的情况
4、提高灵活性,代码能够通过接口灵活调用
5、提高可维护性,修改的地方越少

二、七大原则

1、单一原则:一个类只负责一个职责
2、开闭原则:对修改关闭,对扩展开放
3、里氏替换原则:不要破坏继承关系
4、接口隔离原则:暴露最小接口,避免接口过于臃肿
5、依赖倒置原则:面向抽象编程
6、迪米特 法则:尽量不跟陌生人讲话
7、合成复用原则:多使用组合、聚合、少用继承

1、单一原则

一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序,实现高内聚,低耦合

优点:降低耦合,提高可维护性,对一个类进行单独处理并且不影响其他类

2、开闭原则

对修改关闭,对扩展开放,核心是:抽象化,多态使用
优点:提高可扩展性,提高可维护性,提高可复用性

3、里氏替换原则

所有使用父类的地方,必须能够透明的使用子类对象

例如:父类Animal;子类Dog继承父类Animal,子类Cat继承父类Animal。

Animal animal=newDog();//同样可以透明的使用
Animal animal=newCat();

当子类重写父类的方法时,则不适合使用里氏替换原则。

4、接口隔离原则

每一个接口承担独立的角色,只干自己该干的事情。
只暴露最小接口,实现类不需要用到实现的方法不要放在接口。

5、依赖倒置原则

指的是面向抽象编程,对抽象类或接口进行依赖,比较灵活。
比如:有抽象类Animal,子类Dog继承父类Animal,子类Cat继承父类Animal。在依赖的时候用抽象类,在使用的时候指定子类

//抽象类和子类abstractclassAnimal{abstractvoidhello();}classDogextendsAnimal{@Overridevoidhello(){
		System.out.println("hello ,I am dog");}}classCatextendsAnimal{@Overridevoidhello(){
		System.out.println("hello ,I am cat");}}//依赖到抽象类classClient{
Animal a;publicClient(Animal a){this.a=a;}publicvoidhello(){
		a.hello();}}//测试类进行使用publicclassTest{publicstaticvoidmain(String args[]){//在使用到Animal的使用,需要定义它的子类
   	Client c=newClient(newDog());
    c.hello();//打印指定的方法}}

6、迪米特法则

尽量不要跟陌生人说话。
不是陌生人的情况:

  • 当前对象本身(this)
  • 方法参数
  • 对象的成员对象
  • 对象所创建的对象

目的:降低耦合,高内聚

7、合成复用原则

尽量使用组合、聚合的方式,少使用继承。
原因:继承会导致耦合度变高。
聚合方式的使用案例:

classA{}classTest{
A a;publicvoiduseA(A a){this.a=a;}}

组合方式的使用案例:

classA{}classTest{
	A a=newA();}

组合和聚合的区别 : 组合方式在使用Test对象的时候会立即开辟A对象的空间,而聚合的方式是在使用到A对象的时候才会开辟A对象的空间。

三、23种设计模式

23种设计模式
创建型模式:主要关注点是“怎样创建对象”

1、单例模式(Singleton)

特点:当需求是一个类只需要一个实例
单例模式有:1、饿汉模式;2、懒汉模式;3、双重检查模式;4、枚举模式

饿汉模式(推荐使用)

特点: 当类加载时创建好对象,并且在外部不能通过new创建该对象,只能调用这个类的的方法进行调用。线程安全,在实际开发中用的最多
案例:
方式一

/**
 * 饿汉方式一
 * @Author:小庄
 */publicclassSingleton{//private不允许外部调用new创建对象privateSingleton(){}//使用静态关键字”static“保证只有一次privatestatic Singleton instance=newSingleton();//外部可直接通过类进行调用静态方法publicstatic SingletongetInstance(){return instance;}}/**
 * 饿汉方式二
 * @Author:小庄
 */classSingleton2{privatestatic Singleton2 instance;privateSingleton2(){
        instance=newSingleton2();}/**
 * 饿汉方式二
 * @Author:小庄
 */classSingleton2{privatestatic Singleton2 instance;static{
        instance=newSingleton2();}privateSingleton2(){}//外部可直接通过类进行调用静态方法publicstatic Singleton2getInstance(){return instance;}}

懒汉模式(不推荐使用)

特点: 等需要该类的时候再加载,会考虑线程安全问题
1、不安全的懒汉模式

/**
 * 线程不安全的懒汉模式
 * @Author:小庄
 */classSingleton{privatestatic Singleton instance;privateSingleton(){}publicstatic SingletongetInstance(){
    	instance=newSingleton();return instance;}}classSingleton2{privatestatic Singleton2 instance;privateSingleton2(){}publicstatic  Singleton2getInstance(){if(instance==null){synchronized(Singleton2.class){
	    		instance=newSingleton2();}}return instance;}}

以上代码线程不安全的原因:当几个线程同时访问的时候,就有可能创建多个对象实例。

2、线程安全的懒汉模式

/**
 * 线程安全的懒汉模式
 * @Author:小庄
 */classSingleton{privatestatic Singleton instance;privateSingleton(){}publicstaticsynchronized SingletongetInstance(){if(instance==null){
    		instance=newSingleton();}return instance;}}

以上代码缺点:当getInstance()方法里面的逻辑代码很复杂时,所有的代码都被加锁,会大大的消耗性能

双重检查懒汉模式(线程安全)

/**
 * 线程安全的懒汉模式
 * @Author:小庄
 */classSingleton{privatestatic Singleton instance;privateSingleton(){}publicstatic  SingletongetInstance(){if(instance==null){//进行加锁synchronized(Singleton.class){//再进行一次判断类是否为空if(instance==null){
					instance=newSingleton();}}}return instance;}}

静态内部类实现单例

/**
 *内部类实现单例
 * @Author:小庄
 */classSingleton{//设置为privateprivateSingleton(){}//内部类的特性,外部类不能直接访问privatestaticclassGetSingleton{privatestaticfinal Singleton instance=newSingleton();}public SingletongetInstance(){return GetSingleton.instance;}}

运用枚举实现单例(推荐使用)

enum Singleton2{
    ONE,TWO,THREE;//一个属性代表一个实例,这里有三个实例}

小结

推荐使用饿汉模式和枚举模式实现单例的原因是线程安全,而且简单明了
不推荐使用懒汉模式的原因是:1、部分的懒汉模式不安全 ;2、过于繁琐 ;3、有可能会对性能造成额外消耗

2、工厂模式(Factoy)

特点:对类的创建用一个工厂类进行管理。

工厂模式分为:简单工厂,工厂方法和抽象工厂,这节内容主要讲简单工厂和工厂方法。

简单工厂

由一个工厂对象决定创建出哪一种产品类的实例
代替构造函数创建对象,方法名比构造函数清晰。
简单工厂的类图:

简单工厂
具体代码实现:

publicclassSimpleFactory{publiccreateProduct(int i){switch(i){case0:newProduct1();break;case1:newProduct2();break;case2:newProduct3();break;default: System.out.println("没有该产品");}}//具体的使用者publicstaticvoidmain(String[] args){
		SimpleFactory factory=newSimpleFactory();//通过工厂进行创建类
		factory.createProduct(1);}}classProduct1{}classProduct2{}classProduct3{}

工厂方法

工厂方法

//抽象类abstractclassFactoryMethod{//生产方法publicabstractvoidproduction(String productName);}classProduct1extendsFactoryMethod{//实现抽象方法@Overridepublicvoidproduction(String productName){
		System.out.println("Product1生产了"+productName+"产品");}}classProduct2extendsFactoryMethod{//实现抽象方法@Overridepublicvoidproduction(String productName){
		System.out.println("Product2生产了"+productName+"产品");}}//使用者publicclassClient{publicstaticvoidmain(String[] args){
		FactoryMethod factory=newProduct1();
		factory.production("x");}}

3、抽象工厂模式(AbstractFactory)

创建一组有关联的对象实例

角色:

  • AbstractProduct (抽象产品)
  • AbstractFactory (抽象工厂)
  • Client (委托者)

抽象工厂
具体实现如下,为了保证代码的阅读,这里不对类的具体操作进行展开

//定义抽象工厂类abstractclassAbstractFactory{//定义抽象方法,返回值是抽象产品类对象abstract AbstractClass1createClass1();abstract AbstractClass2createClass2();abstract AbstractClass3createClass3();}//把抽象产品类全部定义完abstractclassAbstractClass1{}abstractclassAbstractClass2{}abstractclassAbstractClass3{}//定义具体产品类,分别继承对应的抽象产品类classClass1extendsAbstractClass1{}classClass2extendsAbstractClass2{}classClass3extendsAbstractClass3{}//定义具体工厂类,实现抽象方法,具体工厂类1classFactory1extendsAbstractFactory{@Override
	AbstractClass1createClass1(){// TODO Auto-generated method stubreturnnewClass1();}@Override
	AbstractClass2createClass2(){// TODO Auto-generated method stubreturnnewClass2();}@Override
	AbstractClass3createClass3(){// TODO Auto-generated method stubreturnnewClass3();}}//具体工厂类2classFactory2extendsAbstractFactory{@Override
	AbstractClass1createClass1(){// TODO Auto-generated method stubreturnnewClass1();}@Override
	AbstractClass2createClass2(){// TODO Auto-generated method stubreturnnewClass2();}@Override
	AbstractClass3createClass3(){// TODO Auto-generated method stubreturnnewClass3();}}

分析

从上面代码中,我们可以发现,抽象工厂处理的是产品一族,它们和工厂方法的区别在于:工厂方法有利于处理一个产品的部件扩展维度,而抽象工厂有利于扩展产品一族维度。

4、原型模式(Prototype)

特点:必须实现Cloneable接口,并且重写clone方法,否则会报错
应用场景:一个对象属性特别多,同时指定很麻烦,实际工作中用的很少

角色:

  • Prototype(原型)
  • ConcretePrototype(具体原型)
  • Client(使用者)

浅克隆

//原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
interface Prototype extends Cloneable{
}
//具体原型类实现原型接口,重写方法
public class ConcretePrototype implements Prototype {
    @Override
    protected Object clone() throws CloneNotSupportedException {
       //通过克隆自身对象
        ConcretePrototype p=(ConcretePrototype)this.clone();
        //返回克隆后的值
        return p;
    }
}

通过clone实现深拷贝

//原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
interface Prototype extends Cloneable{
}
//具体原型类实现原型接口,重写方法
public class ConcretePrototype implements Prototype {
    //假设有一个对象属性,需要克隆这个对象属性
    public Book book;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //对基本属性进行克隆
        Object deep=null;
        deep=super.clone();
        //对引用类型的数据进行单独处理
        ConcretePrototype c=(ConcretePrototype )deep;
        c.book= (Book)book.clone();
        //返回克隆后的值
        return deep;
    }
}

缺点:繁琐
通过序列化实现深拷贝

publicclassBookimplementsSerializable{//浅克隆,使用默认的clone方法public ObjectdeepClone(){

        ByteArrayInputStream bis=null;
        ByteArrayOutputStream bos=null;
        ObjectOutputStream oos=null;
        ObjectInputStream ois=null;try{
            bos=newByteArrayOutputStream();
            oos=newObjectOutputStream(bos);
            oos.writeObject(this);//反序列化
            bis=newByteArrayInputStream(bos.toByteArray());
            ois=newObjectInputStream(bis);
             Book book=(Book)ois.readObject();return book;}catch(IOException| ClassNotFoundException e){
            e.printStackTrace();return null;}finally{try{
                bos.close();
                oos.close();
                bis.close();
                ois.close();}catch(IOException e){
                e.printStackTrace();}}}}

5、建造者模式(Builder)

特点:处理复杂对象的创建,在使用时只需要用想要的属性时,不需要设置所有参数;这个建造者模式和模板方法特别像

角色:

  • Builder(建造者)
  • ConcreteBuilder(具体建造者)
  • Director(监工)
  • Client(使用者)
//建造者接口或抽象类interfaceBuilder{//具体步骤
	BuildercreteBuilder();
	BuildercreteBuilder2();
	BuildercreteBuilder3();}//具体建造类classConcreteBuilderimplementsBuilder{//实现具体步骤,返回值为当前对象,便于链式编程@Overridepublic BuildercreteBuilder(){
		System.out.println("建造第一层");returnthis;}@Overridepublic BuildercreteBuilder2(){
		System.out.println("建造第二层");returnthis;}@Overridepublic BuildercreteBuilder3(){
		System.out.println("建造第三层");returnthis;}}//监工classDirector{//将抽象类(接口)聚合,然后对抽象方法进行调用,会指向子类实现的方法
	Builder builder;publicDirector(Builder builder){this.builder=builder;}publicvoidtoBuilder(){
		builder.creteBuilder().creteBuilder2().creteBuilder3();}}//使用者publicclassClient{publicstaticvoidmain(String[] args){//通过监工直接建造
		Director d=newDirector(newConcreteBuilder());
		d.toBuilder();}}

比较常用的使用方式

//模拟建造者模式的具体使用publicclassPerson{//这里定义了很多的属性,但是有时候只需要部分的属性int id;
	String name;int age;double weight;int score;//将构造函数设置为私有属性,不让外部进行访问privatePerson(){}//定义内部类,通过内部类对类的成员变量进行操作,可以叫它为静态工厂publicstaticclassPersonBuilder{
		Person p=newPerson();//每次方法返回值不为void,而是直接返回当前的对象,有利于链式方程public PersonBuilderbasicInfo(int id,String name,int age){
			p.id=id;
			p.name=name;
			p.age=age;returnthis;}public PersonBuilderweight(double weight){
			p.weight=weight;returnthis;}public PersonBuilderscore(int score){
			p.score=score;returnthis;}//通过下面方法返回外部类对象public Personbuild(){return p;}}//打印publicstaticvoidmain(String[] args){//采用链式编程,直观</
  • 作者:码农_小庄
  • 原文链接:https://blog.csdn.net/weixin_44715643/article/details/120146342
    更新时间:2022-09-21 13:38:28