领域驱动设计DDD之值对象 置顶! 有更新!

值对象是什么?

之前我们讲到实体,它最主要的特征在于概念上的标识。而领域中存在一些不需要进行标识的对象,它们主要是对事物的描述。如何理解这段话呢?可以通俗的理解,之前我们认为实体是数据库中的一条记录,这条记录会发生更改,例如学生的信息表。而值对象对应到数据库中的什么呢?它们不会单独地映射一张表而是可能对应表中的几个字段。比如学生信息表中拥有省、市、区、地址四个字段构成学生的住址,这四个字段构成的是一个整体用来描述学生的详细地址。

为什么我们将学生详细地址建模成值对象呢?

因为我们并不需要追踪学生详细地址的变化,我们并不关系它的连续性。


在很多DDD的博客文章中讲解到值对象都会用到了地址这一概念,可能有的人会误解地址天然就是值对象。并非如此,它有时可能作为值对象,有时可能作为实体。这需要看我们的业务场景。

值对象的情形:

  1. 当你和你的舍友在淘宝上购买商品,你们在订单上填的地址是一样的,并不需要严格区分这两个地址,快递直接送到这个地址即可。(地址映射到订单表中的几个字段上,如:省市区地址)

实体的情形:
2. 当你们宿舍的宽带坏了,需要报修,这时候你和你的舍友都打了电话报修。但是宽带人员并不会派两个人来抢修宽带。因为宽带人员知道你们是同一个地址。(宿舍地址映射到宽带公司系统中的一条地址用户表,以地址来区别用户)


阅读全文 »

Spring AOP 之cglib动态代理剖析 置顶!

Cglib动态代理

在之前的文章中我们介绍了JDK动态代理的解析,今天我们来剖析一下Cglib的动态代理解析。

Cglib代理例子

按照惯例我们先用一个简单的例子来说明

HelloService被代理类:

public class HelloService {
    public void hello() {
        System.out.println("hello!");
    }
}

事务增强类MethodInterceptor:

public class TransactionIntercepter implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务!");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("结束事务!");
        return object;
    }
    
}
阅读全文 »

一文搞懂四种同步工具类 置顶! 有更新!

CountDownLatch

解释:

CountDownLatch相当于一个门闩,门闩上挂了N把锁。只有N把锁都解开的话,门才会打开。怎么理解呢?我举一个赛跑比赛的例子,赛跑比赛中必须等待所有选手都准备好了,裁判才能开发令枪。选手才可以开始跑。CountDownLatch当中主要有两个方法,一个是await()会挂上锁阻塞当前线程,相当于裁判站在起始点等待,等待各位选手准备就绪,一个是countDown方法用于解锁,相当于选手准备好了之后调用countDown方法告诉裁判自己准备就绪,当所有人都准备好了之后裁判开发令枪。

代码:

public class TestCountDownLatch {
    public static void main(String[] args) {
        // 需要等待两个线程,所以传入参数为2
        CountDownLatch latch = new CountDownLatch(2);
        // 该线程运行1秒
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("1号选手准备就绪!用时1秒!");
                latch.countDown();
            }
        }).start();
        
        // 该线程运行3秒
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("2号选手准备就绪!用时3秒!");
                latch.countDown();
            }
        }).start();
        
        try {
            System.out.println("请1号选手和2号选手各就各位!");
            // 主线程在此等待两个线程执行完毕之后继续执行
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 两个线程执行完毕后,主线程恢复运行
        System.out.println("裁判发枪,1号选手和2号选手开跑!");
    }
}

阅读全文 »

同步容器和并发容器总结 置顶! 有更新!

什么是同步容器?

同步容器通过synchronized关键字修饰容器保证同一时刻内只有一个线程在使用容器,从而使得容器线程安全。synchronized的意思是同步,它体现在将多线程变为串行等待执行。(但注意一点,复合操作不能保证线程安全。举例:A线程第一步获取尾节点,第二步将尾结点的值加1,但在A线程执行完第一步的时候,B线程删除了尾节点,在A线程执行第二步的时候就会报空指针)

什么是并发容器?

并发容器指的是允许多线程同时使用容器,并且保证线程安全。而为了达到尽可能提高并发,Java并发工具包中采用了多种优化方式来提高并发容器的执行效率,核心的就是:锁、CAS(无锁)、COW(读写分离)、分段锁。

阅读全文 »

Spring IOC过程源码解析 置顶! 有更新!

废话不多说,我们先做一个傻瓜版的IOC demo作为例子

自定义的Bean定义

class MyBeanDefinition{

    public String id;
    public String className;
    public String value;

    public MyBeanDefinition(String id, String className, String value) {
        this.id = id;
        this.className = className;
        this.value = value;
    }

}
阅读全文 »

DRY、KISS、YAGNI三原则的理解 置顶! 有更新!

软件三原则的个人理解

在软件的设计当中前人已经总结了许多的设计原则和设计模式。例如SOLID,GRASP设计原则,这些原则都是基于面向对象设计总结而来的。而GOF23是基于许多常见的场景总结出了一套设计模式,在我们遇到类似的场景,都可以套用设计模式。而今天所讲到的软件三原则是适用于在软件设计的各个层面的。它不仅适用于面向对象的设计,也适用于面向过程的程序设计;不仅适用于类的设计,也适用于模块、子系统的设计。就连在项目架构运维部署中也适用于这一套简单的法则。

阅读全文 »

GRASP设计原则 置顶! 有更新!

GRASP设计原则

GRASP(General Responsibility Assignment Software Pattern)是通用职责分配软件设计模式。
它由《UML和模式应用》(Applying UML and Patterns)一书作者Craig Larman提出。在面向对象设计的过程中一般的通用方式是构思对象的职责、角色和协作。通常来说,我们在编码过程中先分析问题域,从中抽象出对象解决问题。简单的面向对象和优良的面向对象设计的区别在于将如何更合理的划分对象的角色,给对象赋予合理的职责以及对象之间的交互关系。

在我所理解的GRASP九大原则、SOLID七大原则、GOF23种设计模式这三类设计原则模式中。GRASP处于最上层,SOLID基于它再进一步细化阐述,GOF再根据这些原则进一步的归纳出更具体的模式。
GoF模式是针对特定问题而提出的解决方案,而GRASP站在一个更高的角度来看待面向对象软件的设计,它是GoF设计模式的基础。GRASP是对象职责分配的基本原则,其核心思想是职责分配,用职责设计对象。

阅读全文 »

常见算法总结 - 二叉树篇

本文总结了常见高频的关于二叉树的算法考察。

1.计算一个给定二叉树的叶子节点数目。

可以采用递归的方式进行累加

public static int calculateTreeNodeNumber(TreeNode treeNode) {

        if (treeNode == null) {
            return 0;
        }

        return calculateTreeNodeNumber(treeNode.left) + calculateTreeNodeNumber(treeNode.right) + 1;

}

2.计算二叉树的深度。

跟上题一样采用递归的方式,但需返回左右子树中较深的深度。

public static int getTreeDepth(TreeNode tree) {

        if (tree == null) {
            return 0;
        }

        int left = getTreeDepth(tree.left);
        int right = getTreeDepth(tree.right);

        return left >= right ? left + 1 : right + 1;
}

阅读全文 »

常见算法总结 - 排序篇

本文总结了常见高频的关于排序的算法考察。

1.冒泡排序

冒泡排序的思想是元素两两比较,将较大或者较小的元素往一端进行移动

 public static void bubble(int[] array) {

        for (int i = 0; i < array.length - 1; i++) {

            for (int j = 0; j + 1 < array.length - i; j++) {

                if (array[j] > array[j + 1]) {

                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;

                }

            }

        }

}

阅读全文 »

常见算法总结 - 链表篇

本文总结了常见高频的关于链表的算法考察。

1.如何找到链表的中间元素?

我们可以采用快慢指针的思想,使用步长为1的慢指针和步长为2的快指针,当快指针抵达链表末尾时,此时慢指针指向的即为中点位置。


public static LinkNode findMiddleByPointer(LinkNode node) {

    LinkNode slow = node;
    LinkNode fast = node;
    // 检测快指针是否可以安全移动
    while (fast.next != null && fast.next.next != null) {

        slow = slow.next;
        fast = fast.next.next;

    }

    return slow;

}

我们还可以采用递归的方式,当递归到最末尾的时候,我们已经能知道链表的长度,此时当递归回去的时候,判断当前递归层级等于链表长度一半的时候,即为链表的重点。

public static void findMiddleByRecursion(LinkNode node, int recursionIndex) {

        if (node.next != null) {
            findMiddleByRecursion(node.next, recursionIndex + 1);
        } else {
            middleIndex = recursionIndex % 2 == 0 ? recursionIndex / 2 : recursionIndex / 2 + 1;
        }

        if (middleIndex == recursionIndex) {
            System.out.println(node.value);
        }

        return;

    }
阅读全文 »