Coder Social home page Coder Social logo

blog's Introduction

Hi there 👋

  • 🔭 I’m currently working on Nebo at MyScript.
  • 🌱 I’m currently learning French, .NET, Chinese calligraphy, piano, etc.
  • 👯 I’m looking to collaborate on any cool projects like Open Ink (Windows) based on MyScript Interactive Ink SDK 😉.
  • 🤔 I’m looking for help with getting inspired.
  • 💬 Ask me about ANYTHING.
  • 📫 How to reach me: LinkedIn | ...
  • 😄 Pronouns: He / Him / His.
  • ⚡ Fun fact: I love Disney movies 🤦‍♂️.

blog's People

Contributors

jingkecn avatar

Watchers

 avatar  avatar

blog's Issues

How To: Implement Event-driven Architecture in Java

Introduction

In computing systems, Event-driven Architecture (abbr. EDA,same in the following context) is
a pattern to implement a loosely coupled system.

Inspired by .Net

In .Net, Delegation is the basic pattern to implement EDA, and Event is declared and defined by delegate keyword and event keyword:

// A delegate is a type that represents references to methods 
// with a particular parameter list and return type.
delegate void SampleEventHandler(/* params */);

// The event keyword is used to declare an event in a publisher class.
event SampleEventHandler SampleEvent;

However, an event is supposed to be dispatched by an event target and listened by an event listener.

Event Target

An event target is in charge of:

  • Definition of an event
  • Dispatching an event
/**
 * A sample of event target in C#.
 **/
class SampleEventTarget {
    // Event definition.
    delegate void SampleEventHandler(/* params */);
    event SampleEventHandler SampleEvent;

    // Event dispatching.
    // Dispatch SampleEvent somewhere in member methods.
    //     SampleEvent?.invoke(/* params */);
}

Event Listener

An event listener is in charge of handling event if any invoked.

/**
 * A sample of event listener in C#.
 **/
class SampleEventListener {
    void OnSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

Registration and Cancellation

In general, a central controller in global scope is supposed to be in charge of:

  • Register an event listener to an event target.
  • Cancel an event listener from an event target.
var sampleEventTarget = new SampleEventTarget();
var sampleEventListener = new SampleEventListener();

del lambdaEventHandler = (/* params */) => {
    sampleEventListener.OnSampleEventInvoked(/* params */);
};
// Register lambda event handler to event target.
sampleEventTarget.SampleEvent += lambdaEventHandler;
// Cancel lambda event handler from event target.
sampleEventTarget.SampleEvent -= lambdaEventHandler;

// Register event handler reference to event target.
sampleEventTarget.SampleEvent += sampleEventListener.OnSampleEventInvoked;
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent -= sampleEventListener.OnSampleEventInvoked;

Implementation in Java

Then here comes the question: what can be things corresponding to delegate and event of .Net in Java?

From the observation on previous .Net samples, we can conclude that its delegate declares and defines a signature of the method handling event. Thus functional interface in Java is quite close to it.

// Defines an event handler.
@FunctionalInterface
interface ISampleEventHandler {
    void invoke(/* params */);
}

Then what can be corresponding to event?
Still from the observation on previous .Net samples, an object declared by keyword event has two operators: += and -=, it means that an event should be a collection. Then the elements of this collection should be defined by the delegate: SampleEventHandler

// An event in Java can be a collection of event handlers.
HashSet<ISampleEventHandler> sampleEvent = new HashSet();

Then all we have to do is to copy mechanically:

Event Target

/**
 * A sample of event target in Java.
 **/
class SampleEventTarget {
    // Defines an event handler.
    @FunctionalInterface
    interface ISampleEventHandler {
        void invoke(/* params */);
    }

    // An event in Java can be a collection of event handlers.
    HashSet<ISampleEventHandler> sampleEvent = new HashSet();

    // Event dispatching.
    // Dispatch sampleEvent somewhere in member methods.
    //     sampleEvent.forEach { handler -> handler.invoke(/* params */); };
}

Event Listener

/**
 * A sample of event listener in Java.
 **/
class SampleEventListener {
    void onSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

Registration and Cancellation

SampleEventTarget sampleEventTarget = new SampleEventTarget();
SampleEventListener sampleEventListener = new SampleEventListener();

ISampleEventHandler sampleEventHandler = new ISampleEventHandler() {
    @Override
    void invoke(/* params */) {
        sampleEventListener.onSampleEventInvoked(/* params */)
    }
};
// Register event handler to event target.
sampleEventTarget.sampleEvent.add(sampleEventHandler);
// Cancel event handler from event target.
sampleEventTarget.sampleEvent.remove(sampleEventHandler);

Function</* param types */, Void> lambdaEventHandler = (/* params */) => {
    sampleEventListener.onSampleEventInvoked(/* params */);
    return null;
};
// Register lambda event handler to event target (Java 8+ required).
sampleEventTarget.sampleEvent.add(lambdaEventHandler);
// Cancel lambda event handler from event target.
sampleEventTarget.sampleEvent.remove(lambdaEventHandler);

// Register event handler reference to event target (Java 8+ required).
sampleEventTarget.SampleEvent.add(sampleEventListener::OnSampleEventInvoked);
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent.remove(sampleEventListener::OnSampleEventInvoked);

Conclusion

The most counterintuitive part of implementations suggested by this post lies in:

Lambda Expressions

Lambda expressions were introduced as a new feature in Java 8 to invoke existing methods, so

sampleEventTarget.sampleEvent.add((/* params */) -> { /* ... */ });

is actually a syntax sugar of

sampleEventTarget.sampleEvent.add(new ISampleEventHandler() { /* ... */ });

Method References

Java 8 has also introduced Method References, so that we can get rid of the coerciveness of implementing the functional interface ISampleEventHandler, instead, we can register directly a method as an event handler as long as it has the same signature as defined in ISampleEventHandler.

[!TODO]
Android has recently supported Kotlin, EDA implementation in Kotlin should be much closer to .Net pattern and should also be much more comprehensive.

See Also

No. Link
[1] Wikipedia - Event-driven Architecture
[2] How to: Publish Events that Conform to .NET Framework Guidelines (C# Programming Guide).
[3] The Java™ Tutorials > Method References.

How To: Share Resource Dictionaries in .Net Libraries

Problematic

In .Net development,resource dictionaries are a common way to define objects that we expect to use more than once, such as strings, styles, sizes, etc.
And it is no secret that resources defined in App.xaml of the startup project are shared for all assemblies.
However, when referring to a resource in non-startup project, Visual Studio does not manage to do code navigation and will launch a Resource '*' is not found warning at design time, even if it will work at runtime after deployment, it is not quite practical without the ability of code navigation.

Solution

What Microsoft does not tell us is that an App.xaml of the local library project can be used for design time.
So we just have to add an App.xaml into the library project, and merge the common resource dictionaries in this file as we do for the startup project:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <!-- Libraries -->
        <ResourceDictionary Source="ms-appx:///${COMMON_ASSEMBLY_NAME}/${PATH_TO_RESOURCE}.xaml" />
        <!-- Other merged dictionaries here -->
      </ResourceDictionary.MergedDictionaries>
      <!-- Other app resources here -->
    </ResourceDictionary>
  </Application.Resources>
</Application>

Where:

  • ${COMMON_ASSEMBLY_NAME} is the common library where to put the shared resources.
  • ${PATH_TO_RESOURCE} is an unified access of the shared resources.
+ solution
  + resource.common.lib
    + resource.entry.xaml
  + lib.refer.to.common.resource
    + app.xaml
...

小蟲茶肆觀察誌

小蟲茶肆,於某年某月某日啟於掘金翻譯社區。
然群英會聚時,白氏「🐑三」領數人另起爐灶,楊氏「雪梨」冠以雅稱而得以名之。
吾入肆數年,素以「園長」自稱,意欲仿於《春田花花幼稚園》。
茶肆雖小,卻不乏藏龍臥虎之輩,吾輩當勉之。

白氏

女,字🐑三,駐茶肆京城部,司職不詳,能言法國語。

陳氏

女,字鰻魚,駐茶肆羊城部、京城部,司職於中國百度。

王氏

女,字佳琳,駐茶肆申城部,司職不詳,喜食,常秀廚藝。

楊氏

女,字雪梨,駐茶肆羊城部、鵬城部,司職於中國大疆,亦為肆中知心女神。

陳氏

男,字田田,駐茶肆蓉城部、申城部,司職於中國餓了麼、阿里(注:後阿里收購餓了麼),喜曬貓、賣萌。

李氏

男,字嗷嗷,駐茶肆蓉城部、京城部,司職不詳,喜撕人。

林氏

男,字小叔,駐茶肆京城部,司職不詳,善物化,喜中二。

童氏

男,字童童,駐茶肆申城部、大米粒部,司職於大米粒谷歌,善收割 Offer。

楊氏

男,字波波,駐茶肆加村部,善 ML,喜鰻魚。

葉氏

男,字老馬、老葉,駐茶肆土澳部,善接外包,喜跳槽。

袁氏

男,字蓋倫,駐茶肆申城部,司職於餓了麼,善曬貓、妻,喜懟田田。

園長何人也?

柯氏

男,字克克,駐茶肆法國部,司職於法蘭西 MyScript,喜吃瓜圍觀。

望能與茶肆眾人互勵共勉,以求進步。
園長,書於戊戌元月十五,更於戊戌八月十五。

如何在 Java 中實現「事件驅動架構」

背景

在軟件開發中,「事件驅動架構(簡稱 EDA,下同)」是實現松耦合系統設計的其中一種模式。

源於 .Net 的靈感

在 .Net 中,「委託(Delegation)」是實現 EDA 的基本模式,其「事件(Event)」通過 delegate 關鍵字和 event 關鍵字 聲明定義:

// A delegate is a type that represents references to methods 
// with a particular parameter list and return type.
delegate void SampleEventHandler(/* params */);

// The event keyword is used to declare an event in a publisher class.
event SampleEventHandler SampleEvent;

而「事件」則由「事件監聽目標(Event Target)」觸發(Dispatch),并由「事件監聽器(Event Listener)」監聽(Listen)。

事件監聽目標(Event Target)

一個「事件監聽目標」通常負責:

  • 定義事件(Event Definition)
  • 觸發事件(Event Dispatching)
/**
 * A sample of event target in C#.
 **/
class SampleEventTarget {
    // Event definition.
    delegate void SampleEventHandler(/* params */);
    event SampleEventHandler SampleEvent;

    // Event dispatching.
    // Dispatch SampleEvent somewhere in member methods.
    //     SampleEvent?.invoke(/* params */);
}

事件監聽器(Event Listener)

一個「事件監聽器」則負責在監聽到事件時處理(Handle)事件。

/**
 * A sample of event listener in C#.
 **/
class SampleEventListener {
    void OnSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

註冊(Registration)和註銷(Cancellation)

通常來說,在全局作用域,中控負責:

  • 向事件目標「註冊」事件監聽器
  • 向事件目標「註銷」事件監聽器
var sampleEventTarget = new SampleEventTarget();
var sampleEventListener = new SampleEventListener();

del lambdaEventHandler = (/* params */) => {
    sampleEventListener.OnSampleEventInvoked(/* params */);
};
// Register lambda event handler to event target.
sampleEventTarget.SampleEvent += lambdaEventHandler;
// Cancel lambda event handler from event target.
sampleEventTarget.SampleEvent -= lambdaEventHandler;

// Register event handler reference to event target.
sampleEventTarget.SampleEvent += sampleEventListener.OnSampleEventInvoked;
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent -= sampleEventListener.OnSampleEventInvoked;

在 Java 中實現

那麼在 Java 中,與 .Net 中 delegateevent 相對應的又是什麼呢?
綜上可知,delegate 聲明定義了事件處理方法的函數簽名,那麼這對應了 Java 中的函數式接口(Functional Interface):

// Defines an event handler.
@FunctionalInterface
interface ISampleEventHandler {
    void invoke(/* params */);
}

那麼 event 呢?經觀察,我們可推定,.Net 中的 event 聲明了一個集合(由 +=-= 操作符推測),而其元素為 SampleEventHandler 委託類型:

// An event in Java can be a collection of event handlers.
HashSet<ISampleEventHandler> sampleEvent = new HashSet();

那麼,接下來我們只需要依樣畫葫蘆就好了:

事件監聽目標(Event Target)

/**
 * A sample of event target in Java.
 **/
class SampleEventTarget {
    // Defines an event handler.
    @FunctionalInterface
    interface ISampleEventHandler {
        void invoke(/* params */);
    }

    // An event in Java can be a collection of event handlers.
    HashSet<ISampleEventHandler> sampleEvent = new HashSet();

    // Event dispatching.
    // Dispatch sampleEvent somewhere in member methods.
    //     sampleEvent.forEach { handler -> handler.invoke(/* params */); };
}

事件監聽器(Event Listener)

/**
 * A sample of event listener in Java.
 **/
class SampleEventListener {
    void onSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

註冊(Registration)和註銷(Cancellation)

SampleEventTarget sampleEventTarget = new SampleEventTarget();
SampleEventListener sampleEventListener = new SampleEventListener();

ISampleEventHandler sampleEventHandler = new ISampleEventHandler() {
    @Override
    void invoke(/* params */) {
        sampleEventListener.onSampleEventInvoked(/* params */)
    }
};
// Register event handler to event target.
sampleEventTarget.sampleEvent.add(sampleEventHandler);
// Cancel event handler from event target.
sampleEventTarget.sampleEvent.remove(sampleEventHandler);

Function</* param types */, Void> lambdaEventHandler = (/* params */) => {
    sampleEventListener.onSampleEventInvoked(/* params */);
    return null;
};
// Register lambda event handler to event target (Java 8+ required).
sampleEventTarget.sampleEvent.add(lambdaEventHandler);
// Cancel lambda event handler from event target.
sampleEventTarget.sampleEvent.remove(lambdaEventHandler);

// Register event handler reference to event target (Java 8+ required).
sampleEventTarget.SampleEvent.add(sampleEventListener::OnSampleEventInvoked);
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent.remove(sampleEventListener::OnSampleEventInvoked);

總結

本篇實現的思路中最反直覺的地方有兩處:

Lambda 表達式

Java 8 新增了 lambda 表達式以觸發現有的方法,因此上面 Java 中的

sampleEventTarget.sampleEvent.add((/* params */) -> { /* ... */ });

實際上是

sampleEventTarget.sampleEvent.add(new ISampleEventHandler() { /* ... */ });

的語法糖。

方法引用(Method References)

Java 8 中還新增了「方法引用(Method References)」,因此我們完全可以擺脫 ISampleEventHandler 實現的強制性,直接註冊與 ISampleEventHandler 有相同函數簽名的方法即可。

[!TODO]
Android 新增了 Kotlin 支持,在 Kotlin 中實現相同的「事件驅動架構」會更加接近 .Net 的模式。

參見

No. Link
[1] Wikipedia - Event-driven Architecture
[2] How to: Publish Events that Conform to .NET Framework Guidelines (C# Programming Guide).
[3] The Java™ Tutorials > Method References.

[C#] How To: Use `partial` Keyword To Improve Our Codebase

Problematic

There's always an epic subject of our codebase, when coding in the OOP way, on how to find the balance between the total code lines of a class and the quantity of classes.

When there're too many code lines in a single class, let's say 10K lines for example, you could find it annoying to scroll your source file from top to bottom and vice versa. That's to say, the class is not well designed and it lacks of abstraction, you just put pieces together in this class. A common way to improve our code in this case seems quite straight, it's to either split or encapsulate implementations into several classes, which results in an increment of the quantity of classes.

When there're too many classes in our project, let's say 10M for example, you could also get frustrated to navigate among them, especially when you're reading the codebase or trying to find some APIs somewhere, it's called over-abstraction. So what's next? You're going to regroup them to reduce the quantity of classes?

Analysis

So let's do some math here, by taking the following example:

// MyClass.cs
class MyClass : IMyInterface1, IMyInterface2, ..., IMyInterfaceN
{
}

Let's assume:

  1. MyClass implements N interfaces;
  2. Each interface IMyInterface{K} defines x APIs in average;
  3. Each API requires y code lines in average.

So how many code lines in total are required in MyClass for all API implementations?
It's simple:

$Total = x * y * N$

So if x = 10, y = 100 and N = 10, for example, you will get at least 10K lines in you codebase for MyClass.

The total lines of the codebase depends on 3 variables: x, y and N.
Let's observe them one by one, and find out which variables are stable and which are not.

First of all, N, the number of interfaces to implement.
I would say the value of N is undecided, you'll never know how many interfaces you are should implement along with the evolution of the codebase, so it's an unstable variable that we are not quit sure of its final value.

Then x, the number of APIs that an interface defines.
A well-designed interface, by intuition, is supposed to have a stable number of its APIs, and should not be changed frequently, so let's say, x is definitely stable.

At last, y, the number of code lines that we have to implement an API (method, property, etc.).
This is not easy to see. But as a good practice, we should not have a method with more than 100 lines, so let's say y could a variable with an upper limit.

Therefore, the total lines of MyClass approximates to linearity with N.

Solution

In C#, we can split a class into multiple source files by using the partial keyword.

// MyClass.IMyInterface1.cs
partial class MyClass : IMyInterface1
{
}

// MyClass.IMyInterface2.cs
partial class MyClass : IMyInterface2
{
}

...
// MyClass.IMyInterfaceN.cs
partial class MyClass : IMyInterfaceN
{
}

So there would be x*y lines in average per source file, which results in a relatively stable total number of code lines in each source file.

Moreover, we can also benefit more from this pattern, as is stated in the Microsoft Docs: When working on large projects, spreading a class over separate files enables multiple programmers to work on it at the same time, it's desirable to split a class definition.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.