UML与对象创建型模式

257 阅读9分钟

UML

关联关系(Association)

是类与类之间最常用的一种关系, 它是一种结构化关系,用于表示一类对象与另一类对象之间有联系。

单线实箭头,可自关联

关联关系中要表明对象间的关联关系,如是一对多还是一对一

依赖关系(Dependency)

一种使用关系,例如类A的某个方法需要类B做参数,即要使用类B的方法,用虚线表示

聚合关系(Aggregation)

表示一个整体与部分的关系,成员类是整体类的一部分,即成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立 存在。聚合关系用带空心菱形的直线表示。

组合关系(Composition)

也表示类之间整体和部分的关系, 但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在,部分对象与整体对象之间具有同生共死的关系。 如必须传递某个构造器参数以初始化类,用实心菱形的直线表示。

泛化关系(Generalization)

就是继承关系,用带空心三角形的直线来表示。

实现关系(Realization)

接口与实现的关系,用带空心三角形的虚线来表示。

设计模式

对象创建型模式

工厂方法模式

产品对象通过公共接口实现功能,抽象工厂声明工厂方法由具体子类去实现,工厂的目的是为了返回产品,抽象工厂声明的方法要返回抽象产品,每一个具体产品都对应有一个具体工厂,具体工厂返回具体产品,利用多态的性质,全程使用接口对象声明即可。

模式结构

• Product:抽象产品,产品对象的共同父类或接口

• ConcreteProduct:具体产品 ,实现了抽象产品接口

• Factory:抽象工厂,声明工厂方法,交给子类实现

• ConcreteFactory:具体工厂,实现抽象工厂中的工厂方法,返回一个具体产品类的实例

image.png

利用多态的性质,将通用方法抽象出来放在接口中,调用时更加灵活

所有的具体工厂类都具有同一抽象父类。在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。

public interface Log {
    void writeLog();
}
public class FileLog implements Log {
    @Override
    public void writeLog(){
        System.out.println("我是文件日志写入器");
    }
}
public class DataBaseLog implements Log {
    @Override
    public void writeLog(){
        System.out.println("我是数据库日志写入器");
    }
}
public interface AbstractLogFactory {
    Log createLog();
}
public class FileLogFactory implements AbstractLogFactory{
    @Override
    public Log createLog(){
        return new FileLog();
    }
}
public class DataBaseLogFactory implements AbstractLogFactory {
    @Override
    public Log createLog(){
        return new DataBaseLog();
    }
}
public class FactoryMethodPatternTest {
    public static void main(String[] args){
        AbstractLogFactory abstractLogFactory;

        abstractLogFactory=new FileLogFactory();
        abstractLogFactory.createLog().writeLog();

        abstractLogFactory=new DataBaseLogFactory();
        abstractLogFactory.createLog().writeLog();
    }
}

运行结果为:

我是文件日志写入器
我是数据库日志写入器

抽象工厂模式

弥补工厂方法模式每一种产品都需要新增具体工厂的不足,抽象工厂中声明多种产品工厂方法,能为一个产品家族返回多种属于该家族的具体产品。

当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。

模式结构:

• AbstractFactory:抽象工厂

• ConcreteFactory:具体工厂

• AbstractProduct:抽象产品

• Product:具体产品

同样利用多态性质,符合开闭原则

public interface Connection {
    boolean connection(String url,int port,String password);
}
public class OracleConnection implements Connection{
    @Override
    public  boolean connection(String url,int port,String password){
        System.out.println("已连接:"+url+port);
        return true;
    }
}
public class MySqlConnection implements Connection{
    @Override
    public  boolean connection(String url,int port,String password){
        System.out.println("已连接:"+url+port);
        return true;
    }
}
public interface Statement {
    boolean getStatement();
}
public class OracleStatement implements Statement {
    @Override
    public  boolean getStatement(){
        System.out.println("create Oracle Statement");
        return true;
    }
}
public class MySqlStatement implements Statement {
    public  boolean getStatement(){
        System.out.println("create MySql Statement");
        return true;
    }
}
public interface AbstractFactory {
    Connection createConnection();
    Statement createStatement();
}
public class OracleFactory implements AbstractFactory{
    @Override
    public Connection createConnection(){
        return new OracleConnection();
    }
    @Override
    public Statement createStatement(){
        return new OracleStatement();
    }
}
public class MySqlFactory implements AbstractFactory {
    @Override
    public Connection createConnection(){
        return new MySqlConnection();
    }
    @Override
    public Statement createStatement(){
        return new MySqlStatement();
    }
}

同样使用多态的性质,如果使用工厂方法模式,那么就需要建造两个抽象工厂,四个具体工厂,因为两个抽象产品属于同一个产品家族,所以只使用一个抽象工厂,把生产工厂的方法放在一起即可。缺点是难以扩展抽象产品,因为必须属于同一产品族的产品才可以加入,为了达到单一职责原则,不能在抽象工厂中任意添加产品

建造者模式

将部件和其组装过程分开,一步一步创建一个复杂的对象。即将一个复杂对象的构建与它的表示分离。

也可能存在多个基本部分对象,基本部分对象通过不同的组合可以构建不同的对象,用户使用不同的具体建造者即可得到不同的产品对象。

模式结构

• Director:指挥者 ,用于实际生产产品,内部有方法construct,规定一个产品的构造顺序(即如何构造),通过对基本对象的组合生成一个复杂产品,内部维护建造者Builder,通过construct按顺序建造产品

• Builder:抽象建造者 ,规定建造所有基本对象的方法

• ConcreteBuilder:具体建造者,实现每部分的建造方法

• Product:产品角色

建造者模式的目的仍然是为了解耦,屏蔽某些复杂产品的实现,生成产品的组件由接口声明,由具体的建造者建造,由Director调用组件组合顺序生成产品,允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

//产品及组件
public class Product {
    private ComponentA componentA;
    private ComponentB componentB;
    public Product(ComponentA componentA,ComponentB componentB){
        this.componentA=componentA;
        this.componentB=componentB;
    }
    @Override
    public String toString(){
        return "我是产品";
    }
}

public class ComponentA {
    @Override
    public String toString(){
        return "我是组件A";
    }
}

public class ComponentB {
    @Override
    public String toString(){
        return "我是组件B";
    }
}
//建造者及实现
public interface Builder {
    ComponentA buildA();
    ComponentB buildB();
}
public class ConcreteBuilder implements Builder{
    @Override
    public ComponentA buildA(){
        System.out.println("创建组件A");
        return new ComponentA();
    }
    @Override
    public ComponentB buildB(){
        System.out.println("创建组件B");
        return new ComponentB();
    }
}
//指挥者,规定产品组合顺序,调用建造者组装成产品并返回
public class Director {
    public static Product buildProduct(){
        Builder builder=new ConcreteBuilder();
        return new Product(builder.buildA(),builder.buildB());
    }
}
public class builderPatternTest {
    public static void main(String[] args){
        Product product=Director.buildProduct();
        System.out.println(product.toString());
    }
}
/*运行结果
创建组件A
创建组件B
我是产品
*/

原型模式

对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象

在原型模式结构中定义了一个抽象原型类,所有的Java类都继承 自java.lang.Object,而Object类提供一个clone()方法,可以将 一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。 能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。如果一个类没有实现这个接口但是调用了 clone()方法,Java编译器将抛出一个 CloneNotSupportedException异常。

浅克隆只克隆同一份对象的域的引用,深克隆开辟新的空间并赋值

java中的clone()是深克隆,但copy的内存中如果有引用变量,那么引用的仍然是同一块空间,如果想让引用变量引用的空间也克隆一份,那么最好重写clone()或者自己另写一个创建原型的方法

import java.util.Properties;
public class PrototypePatternTest {
    public static void main(String[] args){
        Properties properties=new Properties();
        properties.put("a",new Properties());
        Properties propertiesClone=(Properties) properties.clone();

        System.out.println(properties==propertiesClone);
        System.out.println(properties.getClass()==propertiesClone.getClass());
        
        //clone的对象引用新的空间
        properties.put("b",2);
        System.out.println(propertiesClone.get("b"));
        
        //但是内部的引用变量仍然引用同一块空间
        ((Properties)properties.get("a")).put("b","2");
        System.out.println(((Properties)propertiesClone.get("a")).get("b"));
    }
}
/*结果为
false
true
null
2
*/

使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率。

单例模式

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:一是某个类只能有一个实例; 二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

模式结构

• Singleton:单例

单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

  • 单例类的构造函数为私有;
  • 提供一个自身的静态私有成员变量;
  • 提供一个公有的静态工厂方法。

确保只有一个对象被实例化的关键在于将构造器私有化,这样外部无法使用new构建对象,也无法通过反射实例化对象,这样一种设计思想确保系统中只有一个实例,提供关键服务,但对于该实例来说内部封装的对象的操作在并发环境下仍要保持原子性,单例模式确保我只有我一个,最常用的便是提供序列化id,系统必须确保每次提供的是唯一id,因此采用单例模式+原子操作(静态方法+原子操作也可以)。

public class Singleton {

    private static Singleton singleton=new Singleton();

    private static volatile long id;
    //单例模式,私有化构造器,避免其他类new该对象
    private Singleton(){

    }
    public static Singleton getSingleton() {
        return singleton;
    }

    public synchronized long getId(){
        return id++;
    }
}
public class SingletonPatternTest {
    public static void main(String[] args){
        //错误,不能访问私有
        //Singleton singleton=new Singleton();
        /*通过反射实例化将抛出java.lang.IllegalAccessException异常
        try {
            Singleton singleton=Singleton.class.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        */
        Singleton singleton=Singleton.getSingleton();
        for(int i=0;i<10;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"我的id:"+singleton.getId());
                }
            }).start();
        }

    }
}