精华内容
下载资源
问答
  • 您可能需要根据您的具体需求进行调整,但它会在视图的顶部绘制一个带有边框的六边形蒙版。背景资源低于掩码。结果:代码:HexagonMaskView.javaimport android.content.Context;import android.graphics.Canvas;...

    尝试这个视图。您可能需要根据您的具体需求进行调整,但它会在视图的顶部绘制一个带有边框的六边形蒙版。背景资源低于掩码。

    结果:

    代码:

    HexagonMaskView.java

    import android.content.Context;

    import android.graphics.Canvas;

    import android.graphics.Color;

    import android.graphics.Path;

    import android.graphics.Region;

    import android.util.AttributeSet;

    import android.view.View;

    public class HexagonMaskView extends View {

    private Path hexagonPath;

    private Path hexagonBorderPath;

    private float radius;

    private float width, height;

    private int maskColor;

    public HexagonMaskView(Context context) {

    super(context);

    init();

    }

    public HexagonMaskView(Context context, AttributeSet attrs) {

    super(context, attrs);

    init();

    }

    public HexagonMaskView(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    init();

    }

    private void init() {

    hexagonPath = new Path();

    hexagonBorderPath = new Path();

    maskColor = 0xFF01FF77;

    }

    public void setRadius(float r) {

    this.radius = r;

    calculatePath();

    }

    public void setMaskColor(int color) {

    this.maskColor = color;

    invalidate();

    }

    private void calculatePath() {

    float triangleHeight = (float) (Math.sqrt(3) * radius / 2);

    float centerX = width/2;

    float centerY = height/2;

    hexagonPath.moveTo(centerX, centerY + radius);

    hexagonPath.lineTo(centerX - triangleHeight, centerY + radius/2);

    hexagonPath.lineTo(centerX - triangleHeight, centerY - radius/2);

    hexagonPath.lineTo(centerX, centerY - radius);

    hexagonPath.lineTo(centerX + triangleHeight, centerY - radius/2);

    hexagonPath.lineTo(centerX + triangleHeight, centerY + radius/2);

    hexagonPath.moveTo(centerX, centerY + radius);

    float radiusBorder = radius - 5;

    float triangleBorderHeight = (float) (Math.sqrt(3) * radiusBorder / 2);

    hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);

    hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY + radiusBorder/2);

    hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY - radiusBorder/2);

    hexagonBorderPath.lineTo(centerX, centerY - radiusBorder);

    hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY - radiusBorder/2);

    hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY + radiusBorder/2);

    hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);

    invalidate();

    }

    @Override

    public void onDraw(Canvas c){

    super.onDraw(c);

    c.clipPath(hexagonBorderPath, Region.Op.DIFFERENCE);

    c.drawColor(Color.WHITE);

    c.save();

    c.clipPath(hexagonPath, Region.Op.DIFFERENCE);

    c.drawColor(maskColor);

    c.save();

    }

    // getting the view size and default radius

    @Override

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    width = MeasureSpec.getSize(widthMeasureSpec);

    height = MeasureSpec.getSize(heightMeasureSpec);

    radius = height / 2 - 10;

    calculatePath();

    }

    }

    更新29.07.2016

    一个更好的方式,只剪辑源图像,而不画整个视图的背景。切换到ImageView作为基类,以受益于scaleType。我也做了一些代码重构。

    import android.content.Context;

    import android.graphics.Canvas;

    import android.graphics.Color;

    import android.graphics.Paint;

    import android.graphics.Path;

    import android.graphics.PorterDuff;

    import android.graphics.Region;

    import android.util.AttributeSet;

    import android.widget.ImageView;

    public class HexagonMaskView extends ImageView {

    private Path hexagonPath;

    private Path hexagonBorderPath;

    private Paint mBorderPaint;

    public HexagonMaskView(Context context) {

    super(context);

    init();

    }

    public HexagonMaskView(Context context, AttributeSet attrs) {

    super(context, attrs);

    init();

    }

    public HexagonMaskView(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    init();

    }

    private void init() {

    this.hexagonPath = new Path();

    this.hexagonBorderPath = new Path();

    this.mBorderPaint = new Paint();

    this.mBorderPaint.setColor(Color.WHITE);

    this.mBorderPaint.setStrokeCap(Paint.Cap.ROUND);

    this.mBorderPaint.setStrokeWidth(50f);

    this.mBorderPaint.setStyle(Paint.Style.STROKE);

    }

    public void setRadius(float radius) {

    calculatePath(radius);

    }

    public void setBorderColor(int color) {

    this.mBorderPaint.setColor(color);

    invalidate();

    }

    private void calculatePath(float radius) {

    float halfRadius = radius / 2f;

    float triangleHeight = (float) (Math.sqrt(3.0) * halfRadius);

    float centerX = getMeasuredWidth() / 2f;

    float centerY = getMeasuredHeight() / 2f;

    this.hexagonPath.reset();

    this.hexagonPath.moveTo(centerX, centerY + radius);

    this.hexagonPath.lineTo(centerX - triangleHeight, centerY + halfRadius);

    this.hexagonPath.lineTo(centerX - triangleHeight, centerY - halfRadius);

    this.hexagonPath.lineTo(centerX, centerY - radius);

    this.hexagonPath.lineTo(centerX + triangleHeight, centerY - halfRadius);

    this.hexagonPath.lineTo(centerX + triangleHeight, centerY + halfRadius);

    this.hexagonPath.close();

    float radiusBorder = radius - 5f;

    float halfRadiusBorder = radiusBorder / 2f;

    float triangleBorderHeight = (float) (Math.sqrt(3.0) * halfRadiusBorder);

    this.hexagonBorderPath.reset();

    this.hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);

    this.hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY + halfRadiusBorder);

    this.hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY - halfRadiusBorder);

    this.hexagonBorderPath.lineTo(centerX, centerY - radiusBorder);

    this.hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY - halfRadiusBorder);

    this.hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY + halfRadiusBorder);

    this.hexagonBorderPath.close();

    invalidate();

    }

    @Override

    public void onDraw(Canvas c) {

    c.drawPath(hexagonBorderPath, mBorderPaint);

    c.clipPath(hexagonPath, Region.Op.INTERSECT);

    c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

    super.onDraw(c);

    }

    @Override

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width = MeasureSpec.getSize(widthMeasureSpec);

    int height = MeasureSpec.getSize(heightMeasureSpec);

    setMeasuredDimension(width, height);

    calculatePath(Math.min(width / 2f, height / 2f) - 10f);

    }

    }

    示例布局:

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    android:background="@android:color/holo_green_dark">

    android:id="@+id/image"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:src="@drawable/bear"

    android:background="@android:color/holo_green_light"/>

    FXPot.png

    展开全文
  • Java的六边形架构

    2021-03-01 11:00:39
    37 } 38 } 39 结论 使用端口和适配器,应用程序域在内部六边形处被隔离,并且无论外部系统或技术如何,它都可以由用户或自动测试脚本同样驱动。 最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在...

    总览

    六角体系结构是一种软件体系结构,它使应用程序可以由用户,程序,自动测试或批处理脚本平等驱动,并且可以独立于其运行时目标系统进行开发。目的是创建一个无需用户界面或数据库即可运行的应用程序,以便我们可以对该应用程序运行自动回归测试,在运行时系统(例如数据库)不可用时使用该应用程序,或无需用户界面即可集成应用程序。

    动机

    许多应用程序有两个目的:用户端和服务器端,通常以两层,三层或n层体系结构设计。n层体系结构的主要问题是没有认真对待层线,从而导致应用程序逻辑越过边界泄漏。业务逻辑和交互之间的这种纠缠使不可能或很难扩展或维护应用程序。

    例如,当应用程序业务逻辑未完全隔离在其自身边界内时,添加新的有吸引力的UI以支持新设备可能是一项艰巨的任务。此外,应用程序可以有两个以上的方面,这使得很难更好地适应一维图层体系结构。

    六角形或端口和适配器或洋葱结构解决了这些问题。在这种体系结构中,内部应用程序通过一定数量的端口与外部系统进行通信。在这里,术语“六角形”本身并不重要,而是表明了在应用程序中以均匀和对称的方式插入端口和适配器的效果。主要思想是通过使用端口和适配器隔离应用程序域。

    在端口和适配器周围组织代码

    让我们构建一个小型的anagram应用程序,以展示如何在端口和适配器周围组织代码以表示应用程序内部和外部之间的交互。在左侧,我们有一个应用程序,例如控制台或REST,而内部则是核心业务逻辑或域。anagram服务采用两个字符串,并返回一个布尔值,该布尔值对应于两个String参数是否彼此为字母。在右侧,我们有服务器端或基础结构,例如,一个用于记录有关服务使用情况的度量标准的数据库。

    下面的Anagram应用程序源代码显示了如何在内部隔离核心域以及如何提供端口和适配器以与其进行交互。

    域层

    域层代表应用程序的内部,并提供与应用程序用例进行交互的端口。

    · IAnagramServicePort 接口定义了一个方法,该方法接受两个String字并返回一个布尔值。

    · AnagramService 实现该IAnagramServicePort接口并提供业务逻辑以确定两个String参数是否为anagram。它还使用IAnagramMetricPort来将服务使用度量输出到服务器端运行时外部实体(例如数据库)。

    应用层

    应用程序层为外部实体与域交互提供了不同的适配器。交互依赖项进入内部。

    · ConsoleAnagramAdaptor 使用IAnagramServicePort来与应用程序内的域进行交互。

    · AnagramsController 还使用IAnagramServicePort与域进行交互。同样,我们可以编写更多的适配器,以允许各种外部实体与应用程序域进行交互。

    基础设施层

    提供适配器和服务器端逻辑,以从右侧与应用程序进行交互。服务器端实体(例如数据库或其他运行时设备)使用这些适配器与域进行交互。请注意,交互依赖项位于内部。

    外部实体与应用程序交互

    以下两个外部实体使用适配器与应用程序域进行交互。如我们所见,应用程序域是完全隔离的,并且由它们平等地驱动,而不管外部技术如何。

    这是一个使用适配器与应用程序域交互的简单控制台应用程序:

    Java

    1

    @Configuration

    2

    public class AnagramConsoleApplication {

    3

    4

    @Autowired

    5

    private ConsoleAnagramAdapter anagramAdapter;

    6

    7

    public static void main(String[] args) {

    8

    Scanner scanner = new Scanner(http://System.in);

    9

    String word1 = scanner.next();

    10

    String word2 = scanner.next();

    11

    boolean isAnagram = anagramAdapter.isAnagram(word1, word2);

    12

    if (isAnagram) {

    13

    System.out.println("Words are anagram.");

    14

    } else {

    15

    System.out.println("Words are not anagram.");

    16

    }

    17

    }

    18

    }

    这是一个简单的测试脚本示例,该脚本使用REST适配器模拟用户与应用程序域的交互。

    Java

    1

    @SpringBootTest

    2

    @AutoConfigureMockMvc

    3

    public class AnagramsControllerTest {

    4

    5

    private static final String URL_PREFIX = "/anagrams/";

    6

    7

    @Autowired

    8

    private MockMvc mockMvc;

    9

    10

    @Test

    11

    public void whenWordsAreAnagrams_thenIsOK() throws Exception {

    12

    String url = URL_PREFIX + "/Hello/hello";

    13

    this.mockMvc.perform(get(url)).andDo(print()).andExpect(status().isOk())

    14

    .andExpect(content().string(containsString("{\"areAnagrams\":true}")));

    15

    }

    16

    17

    @Test

    18

    public void whenWordsAreNotAnagrams_thenIsOK() throws Exception {

    19

    String url = URL_PREFIX + "/HelloDAD/HelloMOM";

    20

    this.mockMvc.perform(get(url)).andDo(print()).andExpect(status().isOk())

    21

    .andExpect(content().string(containsString("{\"areAnagrams\":false}")));

    22

    }

    23

    24

    25

    @Test

    26

    public void whenFirstPathVariableConstraintViolation_thenBadRequest() throws Exception {

    27

    String url = URL_PREFIX + "/11/string";

    28

    this.mockMvc.perform(get(url)).andDo(print()).andExpect(status().isBadRequest()).andExpect(

    29

    content().string(containsString("string1")));

    30

    }

    31

    32

    @Test

    33

    public void whenSecondPathVariableConstraintViolation_thenBadRequest() throws Exception {

    34

    String url = URL_PREFIX + "/string/11";

    35

    this.mockMvc.perform(get(url)).andDo(print()).andExpect(status().isBadRequest()).andExpect(

    36

    content().string(containsString("string2")));

    37

    }

    38

    }

    39

    结论

    使用端口和适配器,应用程序域在内部六边形处被隔离,并且无论外部系统或技术如何,它都可以由用户或自动测试脚本同样驱动。

    最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。

    展开全文
  • 六边形架构 Java 实现

    2021-03-13 05:28:18
    六边形架构是一种设计风格,经过分层实现核心逻辑与外部对象隔离。其核心逻辑是业务模块,外部元素是整合点,好比数据库、外部 API、界面等。它将软件分为内部与外部,内部包含了核心业务逻辑与领域层(所谓分层架构)...

    六边形架构是一种设计风格,经过分层实现核心逻辑与外部对象隔离。其核心逻辑是业务模块,外部元素是整合点,好比数据库、外部 API、界面等。它将软件分为内部与外部,内部包含了核心业务逻辑与领域层(所谓分层架构),外部包含界面、数据库、消息传递及其余内容。内部与外部经过端口和适配器相互通讯。java

    *译注:六边形架构(Hexagonal Architecture)由Alistair Cockburn 提出,解决了传统的分层架构所带来的问题。*git

    1. 优势github

    使用六边形架构开发的软件与通道独立,所以能支持多通道数据库

    易于置换入站和出站整合点架构

    测试软件变得更简单,由于能够很容易地模拟集成点app

    2. Java 实现ide

    按照上面的描述,六边形架构更多地是围绕端口和适配器开展工做。在 Java 中,用 interface 定义端口,实现类做为适配器。下面用一个简单的 Spring Boot 应用示例了解如何应用六边形架构。测试

    示例应用主要功能是建立并查看雇员信息,核心业务逻辑在 `EmployeeService` 中实现,领域对象定义为 `Employee` ,这些均可以看作内部模块。spa

    **EmployeeService.java**

    ```java

    @Service

    public class EmployeeService {

    @Autowired

    private EmployeeRepositoryPort employeeRepository;

    public void create(String name, String role, long salary){

    employeeRepository.create(name, role, salary);

    }

    public Employee view(Integer userId){

    return employeeRepository.getEmployee(userId);

    }

    }

    ```

    **Employee.java**

    ```java

    @Entity

    @Table(name = "employee")

    public class Employee{

    @Id

    @GeneratedValue

    @Column(name = "id")

    private Integer id;

    @Column(name = "name", nullable = false)

    private String name;

    @Column(name = "role", nullable = false)

    private String role;

    @Column(name = "salary", nullable = false)

    private long salary;

    // Setter、Getter 方法

    }

    ```

    如今,示例应用能够经过 REST 或消息机制提供服务。建立实现了`EmployeeUIPort`接口的`EmployeeControllerAdapter`类提供 REST 服务。设计

    **EmployeeControllerAdapter.java**

    ```java

    RestController

    @RequestMapping("/employees/")

    public class EmployeeControllerAdapter implements EmployeeUIPort{

    @Autowired

    private EmployeeService employeeService;

    @Override

    public void create(@RequestBody Employee request){

    employeeService.create(request.getName(), request.getRole(), request.getSalary());

    }

    @Override

    public Employee view(@PathVariable Integer id){

    Employee employee = employeeService.view(id);

    return employee;

    }

    }

    ```

    ```java

    public interface EmployeeUIPort {

    @PostMapping("create")

    public void create(@RequestBody Employee request);

    @GetMapping("view/{id}")

    public Employee view(@PathVariable Integer userId);

    }

    ```

    做为业务逻辑的一部分,`EmployeeService`还须要调用外部 DB 集成点。所以,咱们建立了`EmployeeRepositoryPort`以及实现了该接口的`EmployeeServiceAdapter`。

    **EmployeeServiceAdapter.java**

    ```java

    @Service

    public class EmployeeServiceAdapter implements EmployeeRepositoryPort {

    @PersistenceContext

    private EntityManager entityManager;

    @Transactional

    @Override

    public void create(String name, String role, long salary){

    Employee employee = new Employee();

    employee.setName(name);

    employee.setRole(role);

    employee.setSalary(salary);

    entityManager.persist(employee);

    }

    @Override

    public Employee getEmployee(Integer userId){

    return entityManager.find(Employee.class, userId);

    }

    }

    ```

    **EmployeeRepositoryPort.java**

    ```java

    public interface EmployeeRepositoryPort {

    void create(String name, String role, long salary);

    Employee getEmployee(Integer userId);

    }

    ```

    至此,咱们能够看到 `EmployeeService` 是如何使用 `EmployeeUIPort` 端口提供服务,经过 `EmployeeRepositoryPort` 调用 DB 并经过 `EmployeeControllerAdapter`和`EmployeeServiceAdapter`提供 REST API 服务。

    3. 总结

    总结一下,六边形架构是一种将应用划分红内外两部分的设计方法。经过内部公开的端口与外部实现的适配器进行沟通。应用这种方法,在保持核心用例代码不变的状况下,能够服务多个通道、支持多种不一样协议。 不只如此,它还能有效提升应用程序的可测性。尽管如此,不建议在整个应用中彻底实现六边形架构,而是有选择地使用接口与适配器。

    示例代码可在 [GitHub][1] 中找到。

    [1]:https://github.com/RajeshBhojwani/hexagonal.git

    展开全文
  • 本文的目的是提供一种用Java和Spring以六边形样式实现Web应用程序的自以为是的方式。本文随附GitHub上的示例代码。什么是“六边形架构”?与常见的分层体系结构样式相反,“六角形体系结构”的主要特征是组件之间的...

    本文的目的是提供一种用Java和Spring以六边形样式实现Web应用程序的自以为是的方式。

    本文随附GitHub上的示例代码。

    什么是“六边形架构”?

    与常见的分层体系结构样式相反,“六角形体系结构”的主要特征是组件之间的依赖关系“指向内部”,指向我们的领域对象:

    3a7f7ada349c437f7a5a0c65b0bdce7f.png

    六边形只是一种描述应用程序核心的好方法,该应用程序由领域对象、对其进行操作的用例以及为外界提供接口的输入和输出端口组成。

    领域对象

    在具有业务规则的域中,域对象是应用程序的命脉。域对象可以包含状态和行为。行为与状态越接近,代码就越容易理解,推理和维护。

    域对象没有任何外部依赖性。它们是纯Java,并提供用于用例的API。

    由于域对象不依赖于应用程序的其他层,因此其他层的更改不会影响它们。它们可以不受依赖地演变。这是“单一责任原则”(“ SOLID”中的“ S”)的主要示例,该原则指出组件应该只有一个更改的理由。对于我们的域对象,这是业务需求的变化。

    只需承担一项责任,我们就可以演化域对象,而不必考虑外部依赖关系。这种可扩展性使六角形体系结构样式非常适合您在实践领域驱动设计。在开发过程中,我们只是遵循自然的依赖关系流程:我们开始在域对象中进行编码,然后从那里开始。如果这还不是领域驱动的,那么我不知道是什么。

    用例

    我们知道用例是用户使用我们的软件所做的抽象描述。在六角形体系结构样式中,将用例提升为我们代码库的一等公民是有意义的。

    从这个意义上讲,用例是一个类,它处理某个用例周围的所有事情。例如,让我们考虑用例“银行应用程序中的将钱从一个帐户发送到另一个帐户”。我们将创建一个SendMoneyUseCase具有独特API 的类,该API允许用户转移资金。该代码包含所有针对用例的业务规则验证和逻辑,这些无法在域对象中实现。其他所有内容都委托给域对象(例如,可能有一个域对象Account)。

    与域对象类似,用例类不依赖于外部组件。当它需要六角形之外的东西时,我们创建一个输出端口。

    输入和输出端口

    域对象和用例在六边形内,即在应用程序的核心内。每次与外部的通信都是通过专用的“端口”进行的。

    输入端口是一个简单的接口,可由外部组件调用,并由用例实现。调用此类输入端口的组件称为输入适配器或“驱动”适配器。

    输出端口还是一个简单的接口,如果我们的用例需要外部的东西(例如,数据库访问),则可以用它们来调用。该接口旨在满足用例的需求,但由称为输出或“驱动”适配器的外部组件实现。如果您熟悉SOLID原理,则这是依赖关系反转原理(在SOLID中为“ D”)的应用,因为我们正在使用接口将依赖关系从用例转换为输出适配器。

    有了适当的输入和输出端口,我们就有了非常不同的数据进入和离开我们系统的地方,这使得对架构的推理变得容易。

    转接器Adapter

    适配器形成六角形结构的外层。它们不是核心的一部分,但可以与之交互。

    输入适配器或“驱动”适配器调用输入端口以完成操作。例如,输入适配器可以是Web界面。当用户单击浏览器中的按钮时,Web适配器将调用某个输入端口以调用相应的用例。

    输出适配器或“驱动”适配器由我们的用例调用,例如,可能提供来自数据库的数据。输出适配器实现一组输出端口接口。需要注意的是该接口由用例周围支配,而不是其他方式。

    适配器使交换应用程序的特定层变得容易。如果该应用程序还可以从胖客户端使用到Web,则可以添加胖客户端输入适配器。如果应用程序需要其他数据库,则添加一个新的持久性适配器,该适配器实现与旧的持久性适配器相同的输出端口接口。

    代码演示

    在简要介绍了上面的六边形体系结构样式之后,让我们最后看一些代码。将体系结构样式的概念转换为代码始终受解释和影响,因此,请不要按照给出的以下代码示例进行操作,而应作为创建自己的样式的灵感。

    这些代码示例全部来自我在GitHub上的 “ BuckPal”示例应用程序,并围绕着将资金从一个帐户转移到另一个帐户的用例进行讨论。出于本博客文章的目的,对某些代码段进行了稍微的修改,因此请查看原始代码的存储库。

    1.领域对象

    @AllArgsConstructor(access = AccessLevel.PRIVATE)

    public class Account {

    @Getter private final AccountId id;

    @Getter private final Money baselineBalance;

    @Getter private final ActivityWindow activityWindow;

    public static Account account(

    AccountId accountId,

    Money baselineBalance,

    ActivityWindow activityWindow) {

    return new Account(accountId, baselineBalance, activityWindow);

    }

    public Optional getId(){

    return Optional.ofNullable(this.id);

    }

    public Money calculateBalance() {

    return Money.add(

    this.baselineBalance,

    this.activityWindow.calculateBalance(this.id));

    }

    public boolean withdraw(Money money, AccountId targetAccountId) {

    if (!mayWithdraw(money)) {

    return false;

    }

    Activity withdrawal = new Activity(

    this.id,

    this.id,

    targetAccountId,

    LocalDateTime.now(),

    money);

    this.activityWindow.addActivity(withdrawal);

    return true;

    }

    private boolean mayWithdraw(Money money) {

    return Money.add(

    this.calculateBalance(),

    money.negate())

    .isPositiveOrZero();

    }

    public boolean deposit(Money money, AccountId sourceAccountId) {

    Activity deposit = new Activity(

    this.id,

    sourceAccountId,

    this.id,

    LocalDateTime.now(),

    money);

    this.activityWindow.addActivity(deposit);

    return true;

    }

    @Value

    public static class AccountId {

    private Long value;

    }

    }

    一个Account可以具有许多相关Activitys表示各自表示取款或存款到该帐户。由于我们并不总是希望加载给定帐户的所有活动,因此我们将其限制为一定ActivityWindow。为了仍然能够计算帐户的总余额,Account该类具有baselineBalance在活动窗口开始时包含帐户余额的属性。

    如您在上面的代码中看到的,我们完全没有外部依赖关系地构建了域对象。我们可以自由地对我们认为合适的代码进行建模,在这种情况下,将创建一个非常接近模型状态的“丰富”行为,以使其更易于理解。

    在Account类现在让我们撤出,并把钱存入一个账户,但我们要在两个帐户间转帐。因此,我们创建了一个用例类来为我们精心安排。

    2.建立输入端口

    在实际实现用例之前,我们先为该用例创建外部API,它将成为六边形体系结构中的输入端口:

    public interface SendMoneyUseCase {

    boolean sendMoney(SendMoneyCommand command);

    @Value

    @EqualsAndHashCode(callSuper = false)

    class SendMoneyCommand extends SelfValidating {

    @NotNull

    private final AccountId sourceAccountId;

    @NotNull

    private final AccountId targetAccountId;

    @NotNull

    private final Money money;

    public SendMoneyCommand(

    AccountId sourceAccountId,

    AccountId targetAccountId,

    Money money) {

    this.sourceAccountId = sourceAccountId;

    this.targetAccountId = targetAccountId;

    this.money = money;

    this.validateSelf();

    }

    }

    }

    通过调用sendMoney(),我们应用程序核心外部的适配器现在可以调用此用例。

    我们将所需的所有参数汇总到SendMoneyCommand值对象中。这使我们能够做输入验证的值对象的构造。在上面的示例中,我们甚至使用了Bean Validation批注@NotNull,该批注已在validateSelf()方法中进行了验证。这样,实际的用例代码就不会被嘈杂的验证代码所污染。

    3. 建立用例和输出端口

    在用例实现中,我们使用域模型从源帐户中提取资金,并向目标帐户中存款:

    @RequiredArgsConstructor

    @Component

    @Transactional

    public class SendMoneyService implements SendMoneyUseCase {

    private final LoadAccountPort loadAccountPort;

    private final AccountLock accountLock;

    private final UpdateAccountStatePort updateAccountStatePort;

    @Override

    public boolean sendMoney(SendMoneyCommand command) {

    LocalDateTime baselineDate = LocalDateTime.now().minusDays(10);

    Account sourceAccount = loadAccountPort.loadAccount(

    command.getSourceAccountId(),

    baselineDate);

    Account targetAccount = loadAccountPort.loadAccount(

    command.getTargetAccountId(),

    baselineDate);

    accountLock.lockAccount(sourceAccountId);

    if (!sourceAccount.withdraw(command.getMoney(), targetAccountId)) {

    accountLock.releaseAccount(sourceAccountId);

    return false;

    }

    accountLock.lockAccount(targetAccountId);

    if (!targetAccount.deposit(command.getMoney(), sourceAccountId)) {

    accountLock.releaseAccount(sourceAccountId);

    accountLock.releaseAccount(targetAccountId);

    return false;

    }

    updateAccountStatePort.updateActivities(sourceAccount);

    updateAccountStatePort.updateActivities(targetAccount);

    accountLock.releaseAccount(sourceAccountId);

    accountLock.releaseAccount(targetAccountId);

    return true;

    }

    }

    基本上,用例实现从数据库中加载源帐户和目标帐户,锁定帐户,以便不能同时进行其他任何事务,进行提款和存款,最后将帐户的新状态写回到数据库。

    另外,通过使用@Component,我们使该服务成为Spring Bean,可以注入到需要访问SendMoneyUseCase输入端口的任何组件中,而不必依赖于实际的实现。

    为了从数据库中加载和存储帐户,实现取决于输出端口LoadAccountPort和UpdateAccountStatePort,这是我们稍后将在持久性适配器中实现的接口。

    输出端口接口的形状由用例决定。在编写用例时,我们可能会发现我们需要从数据库中加载某些数据,因此我们为其创建了输出端口接口。这些端口当然可以在其他用例中重复使用。在我们的例子中,输出端口如下所示:

    public interface LoadAccountPort {

    Account loadAccount(AccountId accountId, LocalDateTime baselineDate);

    }

    public interface UpdateAccountStatePort {

    void updateActivities(Account account);

    }

    构建一个Web适配器

    借助域模型,用例以及输入和输出端口,我们现在已经完成了应用程序的核心(即六边形内的所有内容)。但是,如果我们不将其与外界联系起来,那么这个核心将无济于事。因此,我们构建了一个适配器,通过REST API公开了我们的应用程序核心:

    @RestController

    @RequiredArgsConstructor

    public class SendMoneyController {

    private final SendMoneyUseCase sendMoneyUseCase;

    @PostMapping(path = "/accounts/send/{sourceAccountId}/{targetAccountId}/{amount}")

    void sendMoney(

    @PathVariable("sourceAccountId") Long sourceAccountId,

    @PathVariable("targetAccountId") Long targetAccountId,

    @PathVariable("amount") Long amount) {

    SendMoneyCommand command = new SendMoneyCommand(

    new AccountId(sourceAccountId),

    new AccountId(targetAccountId),

    Money.of(amount));

    sendMoneyUseCase.sendMoney(command);

    }

    }

    如果您熟悉Spring MVC,您会发现这是一个非常无聊的Web控制器。它只是从请求路径中读取所需的参数,将其放入SendMoneyCommand并调用用例。例如,在更复杂的场景中,Web控制器还可以检查身份验证和授权,并对JSON输入进行更复杂的映射。

    上面的控制器通过将HTTP请求映射到用例的输入端口来向世界展示我们的用例。现在,让我们看看如何通过连接输出端口将应用程序连接到数据库。

    构建持久性适配器

    输入端口由用例服务实现,而输出端口由持久性适配器实现。假设我们使用Spring Data JPA作为管理代码库中持久性的首选工具。一个实现输出端口的持久性适配器LoadAccountPort,UpdateAccountStatePort然后可能如下所示:

    @RequiredArgsConstructor

    @Component

    class AccountPersistenceAdapter implements

    LoadAccountPort,

    UpdateAccountStatePort {

    private final AccountRepository accountRepository;

    private final ActivityRepository activityRepository;

    private final AccountMapper accountMapper;

    @Override

    public Account loadAccount(

    AccountId accountId,

    LocalDateTime baselineDate) {

    AccountJpaEntity account =

    accountRepository.findById(accountId.getValue())

    .orElseThrow(EntityNotFoundException::new);

    List activities =

    activityRepository.findByOwnerSince(

    accountId.getValue(),

    baselineDate);

    Long withdrawalBalance = orZero(activityRepository

    .getWithdrawalBalanceUntil(

    accountId.getValue(),

    baselineDate));

    Long depositBalance = orZero(activityRepository

    .getDepositBalanceUntil(

    accountId.getValue(),

    baselineDate));

    return accountMapper.mapToDomainEntity(

    account,

    activities,

    withdrawalBalance,

    depositBalance);

    }

    private Long orZero(Long value){

    return value == null ? 0L : value;

    }

    @Override

    public void updateActivities(Account account) {

    for (Activity activity : account.getActivityWindow().getActivities()) {

    if (activity.getId() == null) {

    activityRepository.save(accountMapper.mapToJpaEntity(activity));

    }

    }

    }

    }

    适配器实现已实现的输出端口所需的loadAccount()和updateActivities()方法。它使用Spring Data存储库从数据库加载数据并将数据保存到数据库,并使用域对象AccountMapper映射Account到AccountJpaEntity表示数据库中帐户的对象。

    再次,我们使用@Component它作为Spring bean,可以将其注入到上述用例服务中。

    如果我们要构建一个仅存储和保存数据的CRUD应用程序,则这种架构可能会产生开销。如果我们正在构建具有可以在结合了状态与行为的丰富域模型中表达的丰富业务规则的应用程序,那么该体系结构确实会发光,因为它将域模型置于事物的中心。

    如果您想更深入地研究这个主题,请看一看我的书,它会更详细,并讨论诸如测试,映射策略和快捷方式之类的内容。

    展开全文
  • 本文从理论上推导了正六边形封闭港湾内各共振参数的解析表达式,并采用Boussinesq模型模拟了正六边形及其扩展形状港湾内的水波共振现象,验证了提出的理论,并进一步研究了各共振模态的空间能量分布特征。...
  • android正六边形按钮

    2016-02-26 17:49:39
    android正六边形按钮 SexangleView
  • PS怎么将自己的形状存储为自定形状? 作者:佚名 字体:[增加减小] 来源:互联网 时间:11-16 17:08:54我要评论 自己画的图形怎么添加到自定义形状里?怎么保存?很多朋友都不知道怎么操作,其实方法很简单的。下面小...
  • JavaScript语言:JaveScriptBabelCoffeeScript确定/*** Created by nico on 19/11/2016.*/function lerp(t, a, b) {return a + t * (b - a);}/*** checks if a value has a given bit set to 1* @value the int / ...
  • 相关函数的六边形

    2020-03-24 08:38:53
    基本策略是将它们分解为称为六边形形状因数的基本构建基块,较早时引入这些基块来使用可积性研究结构常数。 分解类似于黎曼曲面的三角剖分,因此我们将其称为六边形化。 我们提出了一套基于对称性将六边形粘合在...
  • 六边形 概述 六边形是一种以方案编写的网络化多人抽象虚拟桌面和语义平铺系统。 用法 六角形取决于鸡肉5.2.0。 安装依赖项,并使用以下命令编译六角客户端和服务器: chicken-install -s -n 启动客户端和服务器:...
  • <br> 这是三张白色六边形组合蜂窝形状PPT背景图片。.PPTX格式;
  • 我是一個完整的初學者,我已經在實踐中進行了一個使用java的小型項目,並且我有點卡住了。我已經通過堆棧交換進行了瀏覽,但是我無法找到一個好的,新手友好的解釋來說明如何在某種模式下繪製簡單的多邊形。...
  • 与以前的工作一样,我们将相关函数分解为六边形形状因子,并使用取决于交叉比率的权重因子将它们粘合起来。 与先前论文中研究的四点函数相比,计算中的主要新成分是两粒子镜面贡献。 我们开发了评估它的技术,并且...
  • If for example, I have the following coordinates with corresponding colorswhich represent a hexagonal shaped grid of hexagons:coord = [[0,0,0],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0],[1,0,-1]]col...
  • android实现六边形布局

    热门讨论 2013-10-31 09:25:52
    android实现六边形等不规则布局,android不规则按钮布局
  • 本文介绍了四环方形,圆形和六边形的光子晶体光纤(PCF)几何形状,用于分析波长范围为800 nm至1600 nm的不同光学特性。 这三种几何类型已用于分析有效面积,传播常数,限制损耗和波导色散。 选择石英玻璃作为背景...
  • 我们使用最近提出的六边形自举程序研究平面N $$ \ mathcal {N} $$ = 4 super-Yang-Mills理论中的重轻(HHL)三点函数。 我们证明了Bajnok,Janik和Wereszczynski [2]的猜想对HHL结构的多项式L-依赖性的影响一直到...
  • 六键 HexaKey 是一种键盘,它试图让在... 此外,不必使用矩形键矩阵来尊重 QWERTY 布局,出现使用六边形形状的可能性,这使键最大 20% 并减少键之间的中间距离。 ======== HexaKey es un teclado que trata de faci
  • 六边形消除

    2021-06-11 10:58:33
    应用介绍在六边形消除的世界里找回沉寂已久的童年神武记忆!爽快的指尖旋风操作,华丽的游戏画面,刺激的六角消除关卡设计,经典的俄罗斯方块消除玩法,无时无刻不给你带来魔法方块体验与消除的魅力。作为方块世界的...
  • 好吧,事实证明这比我想象的要复杂得多.基本问题是最高的Z-ordered按钮总是获得触摸事件,但是即使它没有处理事件,也不会在较低的Z-order上传递它.要获得两个按钮来查看事件,需要解决方法.这是基本方法:1:在片段/...
  • If for example, I have the following coordinates with corresponding colorswhich represent a hexagonal shaped grid of hexagons:coord = [[0,0,0],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0],[1,0,-1]]col...
  • 纯css绘制六边形

    2021-06-11 17:53:59
    .sharp:before {content:"";/*不需要展现文字等内容,所以设置为空字符 */width:0px;border-bottom:80px solid transparent;border-top:80px solid transparent;border-right:40px solid orangered;...
  • 先看一下结果:在之前要先了解一下正六边形内角和边的关系,正六边形的每个内角是60deg,如图(√3其实是根号3):方法一:原理把正六边形分成三部分,左中右分别是:before部分,div部分,after部分,如图:before...
  • 插件描述:鼠标悬停显示六变形矩阵,蜂窝状图片更新时间:2020-01-02 22:13:12//页面渲染varstr=''for(vari=0;istr+=''+''+''+''+'内容'+''+''};...//显示效果$('.imgBox>.hex').hover(function(e){$(th...
  • 这次给大家带来css怎样做出六边形图片,css做出六边形图片的注意事项有哪些,下面就是实战案例,一起来看一下。本文主要介绍了css实现六边形图片的示例代码,分享给大家,具体如下:不说别的,先上效果:用简单的p...
  • 我们在平面4d保形鱼网理论中研究六角形形式... 基于此关系,我们获得了一大类状态的六边形形状因数,包括BMN真空,一些激发态和拉格朗日密度。 我们将这些形状因子应用于多个相关器的计算中,并将结果与​​direc相匹配
  • python画六边形

    千次阅读 2021-03-27 14:05:59
    python画六边形 图片 python代码 import turtle turtle.penup() turtle.goto(-90,90) turtle.pendown() turtle.pensize(5) # 画笔宽度 turtle.pencolor("red") # 画笔颜色 for i in range(3): turtle.fd(180) #...
  • 一、前言六边形能力图如下,由 6 个 六边形组成,每一个顶点代表其在某一方面的能力。这篇文章我们就来看看如何基于 canvas 去绘制这么一个六边形能力图。当然,你也可以基于其他开源的 js 方案来实现,如 EChars.js...
  • 六边形的实现原理其实就是通过旋转三个重叠的矩形得到的,如下图所示:这里为了得到一个正的六边形,两个矩形旋转的角度必须为-60deg和60deg,以及矩形高宽比必须是Math.sqrt(3) : 1那么首先我们要创建三个矩形:...
  • Problem Description A bee larva living in a hexagonal cell of a large honeycomb decides to creep for a walk. In each “step” the larva may move into any of the six adjacent cells and after n steps, ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,017
精华内容 1,206
关键字:

六边形形状