kotlin编写后台

by Adam Arold

亚当·阿罗德(Adam Arold)

在Kotlin编写图书馆的提示 (Tips for Writing a Library in Kotlin)

Writing a library in Kotlin seems easy but it can get tricky if you want to support multiple platforms. In this article we’ll explore ways for dealing with this problem.

用Kotlin编写库似乎很容易,但是如果要支持多个平台,可能会变得很棘手。 在本文中,我们将探讨解决此问题的方法。

Why would I write a library in Kotlin? you might ask. If you have been using Kotlin for a while now you might think that you have everything within the Java ecosystem and it is not necessary to write anything new in Kotlin (apart from the application you are working on).

我为什么要在Kotlin编写图书馆? 你可能会问。 如果您已经使用Kotlin一段时间了,您可能会认为您已经在Java生态系统中拥有了一切,并且不需要用Kotlin编写任何新东西(除了您正在开发的应用程序之外)。

While Kotlin started out as a JVM language, now it can run on multiple platforms and you can even have isomorphic Kotlin in your project. As I have written about this before, Kotlin now can be used with Gradle in place of Groovy, on the frontend and also in your backend projects.

Kotlin最初是一种JVM语言,但现在它可以在多个平台上运行,甚至在项目中甚至可以包含同构的Kotlin。 正如我之前所写,Kotlin现在可以在前端和后端项目中与Gradle一起代替Groovy一起使用。

With Kotlin multiplatform projects and Coroutines getting out of beta, we now have everything at our disposal to write production-grade code which is independent of the platform (JVM, Javascript, Native) it runs on.

随着Kotlin 跨 平台项目和Coroutines脱离beta版本,我们现在可以随意编写生产级代码,而该代码与运行平台无关(JVM,Javascript,Native)。

There is one thing which stands in the way though: the lack of mature multiplatform libraries. So if you want to help with this effort this article is for you.

但是,有一点阻碍:缺少成熟的多平台库 。 因此,如果您想为此做些帮助,那么本文适合您。

那么什么是图书馆? (So What is a Library?)

Now that we decided to write a library it is useful to define what a library really is.

现在,我们决定编写一个库,定义一个库真正是有用的。

Note that this is just my opinion, I’m not an authority figure on the topic. Feel free to point out if I missed something or if you just simply disagree, in the comments section.

请注意,这只是我的观点,我不是该主题的权威人物。 请随意在评论部分中指出我是否错过了什么,或者您只是不同意。

In my definition a library is

在我看来,图书馆是

  • Generic program code通用程序代码
  • Written to perform a single task编写执行单个任务
  • Bundled in a package捆绑包装
  • And distributed to the public in this form并以这种形式分发给公众

Apache Commons IO is a good example for this. We add it to our projects as a dependency, we use its functions, but it doesn’t try to change how we structure our project or write our code.

Apache Commons IO就是一个很好的例子。 我们将其作为依赖项添加到我们的项目中,我们使用其功能,但它不会尝试更改我们构造项目或编写代码的方式。

什么不是图书馆? (What is Not a Library?)

A framework! A good example for this is Ruby on Rails. It is a framework which specifies for you how to code and how to structure your project. In this article we’re going to talk about libraries.

一个框架! Ruby on Rails是一个很好的例子。 它是一个框架,可为您指定如何编码以及如何构建项目。 在本文中,我们将讨论

注意事项 (Things to Keep in Mind)

Whenever you sit down to write a library there are some general guidelines which are applicable in any domain, not just for Kotlin ones.

每当您坐下来编写库时,都有一些通用准则适用于任何领域,而不仅仅是Kotlin准则。

保持API小 (Keep Your API Small)

Giving an API to your users is similar to asking someone to hit a target with an arrow. The smaller the target, the harder it is to hit. This is true with libraries as well. The smaller the API you have, the easier it is to maintain, and you also minimize the chance that you accidentally expose something which you didn’t intend to. Take this API for example:

向用户提供API类似于要求某人用箭头击中目标。 目标越小,击中的难度就越大。 库也是如此。 您使用的API越小,维护起来就越容易,并且还最大程度地减少了意外暴露您不想要的内容的机会。 以这个API为例:

In the following examples we’re going to work on an imaginary library which exposes an API for handling UI widgets.

在以下示例中,我们将在一个虚构的库上工作,该库公开了用于处理UI小部件的API。

Here we expose the MyComponent class which has a property to tell whether it is focused, it exposes its children, and also the draw surface which is used for rendering.

在这里,我们公开MyComponent类,该类具有一个属性来告诉它是否聚焦,它公开其子级以及用于渲染的绘制表面。

There are a lot of cases when you might have the feeling that you need to expose things because you think that it might be useful for you users. What actually happens most of the time is that you expose too much, your users start to rely on them, and later when you figure out that some of the internals of your library need to be refactored you have to break the API for your users if you want to fix it.

在很多情况下,您可能会觉得有必要公开事物,因为您认为它可能对用户有用。 实际上,大多数情况下实际发生的情况是您暴露了太多信息,用户开始依赖它们,后来当您发现需要重构库的某些内部结构时,如果出现以下情况,则必须破坏用户的API:您想修复它。

Let’s take a look at the same class with a smaller API:

让我们看一下具有较小API的同一类:

So after evaluating our class it turns out that

因此,在评估了我们的课程后,事实证明

  1. isFocused was not needed at all and we only want our users to be able to either clear or request the focus. With this we preserved the same functionality but retained the ability to handle focus in any way we wish.

    完全不需要isFocused ,我们只希望我们的用户能够clearrequest焦点。 这样,我们保留了相同的功能,但保留了以我们希望的任何方式处理焦点的功能。

  2. drawSurface is something which we take as a dependency but it is internal to our library so we shouldn’t allow the external world to tamper with it. It happens quite often that users start to use things which you exposed in ways you didn’t intend it to work so this helps with that problem.

    drawSurface是我们依赖的东西,但它在我们库的内部,因此我们不应该允许外部世界对其进行篡改。 用户经常以您不希望其工作的方式开始使用您公开的内容,因此可以解决该问题。

  3. It also turns out that render and attachTo had the same problem as drawSurface: they are internal to the library and the users shouldn’t do rendering or component (re)attaching by hand.

    这也证明, renderattachTo有同样的问题,因为drawSurface :他们是内部的库和用户不应该做渲染或成分(Re)手工安装。

Let’s take this a step further by introducing better abstractions.

通过引入更好的抽象,让我们更进一步。

保持API的抽象和整洁 (Keep Your API Abstract and Clean)

In the previous example we’ve cleaned up parts of our API and removed / hid some things which were not intended to be public. Now we’ll take a look at things we do expose:

在前面的示例中,我们清理了API的一部分,并删除/隐藏了一些不希望公开的内容。 现在,我们来看一下我们公开的内容

After careful investigation we concluded that:

经过仔细调查,我们得出以下结论:

  1. We don’t really need the functionality Lists provide for our children and an Iterable will suffice. A good example for this is that if you expose a List of something which your users just want to iterate over with a for loop, you either lose the ability to construct items on the fly or make it much harder to implement. With an Iterable or a Sequence you can do this easily. It also enables you to return an insanely high number of elements without filling up the memory.

    我们实际上并不需要Listchildren提供的功能,并且Iterable就足够了。 一个很好的例子是,如果您公开了用户仅想通过for循环进行迭代的内容的List ,则可能会失去即时构造项目的能力,或者使其难以实施。 使用IterableSequence您可以轻松地做到这一点。 它还使您可以返回大量的元素,而无需占用内存。

  2. As it turns out PixelGraphicsImpl is a concrete implementation of DrawSurface and it has some internal things which we don’t want to expose. It can become problematic if we expose implementation classes through the API and make it impossible to change the implementation behind the scenes without breaking your users’ code.

    事实证明, PixelGraphicsImplDrawSurface的具体实现,它具有一些我们不想公开的内部功能。 如果我们通过API公开实现类,并且在不破坏用户代码的情况下无法在后台更改实现,则可能会引起问题。

All of these lead us to the realization that to clean up this mess we should start to…

所有这些使我们认识到,要清理这一混乱局面,我们应该开始……

使用界面 (Use Interfaces)

By taking a hard look at what we have, we can conclude that by exposing classes and concrete implementations through our API will lead to all sorts of problems, so using interfaces is a better approach overall:

通过仔细研究我们所拥有的东西,我们可以得出结论,通过我们的API公开类和具体实现会导致各种问题,因此使用接口总体上是一种更好的方法:

This way we are free to implement Component as we see fit. We can have any number of implementations for it if we want and it won’t affect our users. An important caveat for this is to only return abstract types from our factories:

这样,我们可以自由地实现我们认为合适的Component 。 如果需要,我们可以有多种实现方式,并且不会影响用户。 重要的警告是仅从工厂返回抽象类型:

This might seem obvious, but this is often overlooked.

这看起来似乎很明显,但是却经常被忽略。

模块化问题 (The Modularization Problem)

So we separated our API and our concrete implementations into interfaces and classes. The problem is that we can’t prevent our users from circumventing our clean API and using MyComponent directly since Kotlin doesn’t have its own module system. What we can do is to separate our packages into api and internal (or anything similar) and clearly state in the documentation that everything in internal is subject to change:

因此,我们将API和具体的实现分为接口和类。 问题在于,由于Kotlin没有自己的模块系统,我们不能阻止用户绕过我们的干净API并直接使用MyComponent 。 我们可以做的是将我们的程序包分为apiinternal (或类似的东西),并在文档中明确指出internal所有内容都可能发生变化:

This solution is not perfect, but it helps.

该解决方案不是完美的,但可以帮助您。

Kotlin提示 (Kotlin Tips)

We’ve discussed a lot of things already, but we haven’t seen any Kotlin-specific tips yet, so let’s take a look at some.

我们已经讨论了很多事情,但是还没有看到任何Kotlin特有的技巧,因此让我们来看一些。

添加companion object (Add a companion object)

It might be a case that you don’t use companion objects in your project or you don’t have the need for them in some API classes. What’s important to point out here is that companion objects enable your users to define extension functions on your classes which can be invoked without an instance. You can add an empty companion object:

在某些情况下,您可能不会在项目中使用companion object ,或者在某些API类中不需要它们。 这里要指出的重要一点是, companion object使用户可以在类上定义扩展函数,而无需实例即可调用它们。 您可以添加一个空的companion object

and your users gain the ability to augment your interface as they see fit:

并且您的用户可以根据自己的需要扩展您的界面:

将扩展功能更上一层楼 (Take Extension Functions to the Next Level)

Extension functions can also help you to create a more fluent API. Take a look at this example, where our user has a list of Subscriptions:

扩展功能还可以帮助您创建更流畅的API。 看一下这个例子,我们的用户有一个Subscription的列表:

In order to cancel them all they most probably write something like this:

为了取消它们,他们最有可能写这样的东西:

But what if we provide this functionality out of the box?

但是,如果我们提供现成的功能呢?

This way cancelAll can be called on any MutableList which holds Subscriptions:

这样,可以在任何包含SubscriptionMutableList上调用cancelAll

reified功能委派工作 (Have reified Functions Delegate Work)

reified functions are very useful but they come with some caveats which are very frustrating. One of them is that we need to use @PublishedApi if we want to access the internals of a class. For this reason it helps greatly if we simply delegate the work from them to functions which take KClass objects as parameters so we get the utility of reified functions without the problems:

reified功能非常有用,但附带一些警告,令人非常沮丧。 其中之一是,如果要访问类的内部,则需要使用@PublishedApi 。 出于这个原因,它可以帮助极大,如果我们简单地从委托他们的工作,这需要功能KClass对象作为参数,所以我们得到的效用reified功能,而问题:

Astute readers might spot the problem with this API. We’re not using interfaces! Unfortunately interfaces don’t support reified functions, but there is a solution which solves this problem:

精明的读者可能会发现此API的问题。 我们没有使用接口 ! 不幸的是, interface不支持reified功能,但是有一个解决方案可以解决此问题:

reified功能为扩展功能 (Let reified Functions be Extension Functions)

It is true that we can’t have reified functions in an interface:

的确,我们不能在interface使用reifiedreified功能:

but we can have reified extension functions:

但是我们可以使用reified扩展功能:

https://gist.github.com/adam-arold/19ade46f1bce1f4d58cc5ac63e230885

https://gist.github.com/adam-arold/19ade46f1bce1f4d58cc5ac63e230885

With this we get the best of both worlds, and usage stays the same:

这样,我们可以兼得两全其美,使用率保持不变:

The tips above are applicable on any Kotlin project but there is a special kind of project which needs more care than a regular one:

上面的提示适用于任何Kotlin项目,但是有一种特殊的项目需要比常规项目更多的注意:

多平台图书馆 (Multiplatform Libraries)

If you are working on a multiplatform library you need to write code which is idiomatic on all platforms. In the following section we’ll take a look at some tips which will help with this.

如果您正在使用多平台库,则需要编写在所有平台上都是惯用的代码。 在下一节中,我们将介绍一些有助于此操作的技巧。

使用属性代替吸气剂 (Use Properties Instead of Getters)

Writing a getter (getX) for a property is not idiomatic in Kotlin. On the other hand accessing fields in Java without getters is not idiomatic either! It turns out that Kotlin properties are implemented in a way that both sides will see an API they wish to see:

在Kotlin中,为属性编写getter( getX )并不是习惯做法。 另一方面,不使用getter来访问Java中的字段也不是习惯! 事实证明,Kotlin属性是以双方都能看到他们希望看到的API的方式实现的:

隐藏Kotlin API (Hiding a Kotlin API)

Sometimes you have functions which look weird for Java users. A good example for this is a lambda which has to return Unit. Having to return Unit for Java users is just weird. Luckily we have some ways to hide things from Java users:

有时,您所拥有的功能对于Java用户而言似乎很奇怪。 一个很好的例子是必须返回Unit的lambda。 必须为Java用户返回Unit太奇怪了。 幸运的是,我们有一些方法可以向Java用户隐藏事物:

This is nice but what if I want to…

很好,但是如果我想...

隐藏Java API (Hide a Java API)

Unfortunately there is no “official” way of hiding something from Kotlin users, but there is a hack which we can use:

不幸的是,没有向Kotlin用户隐藏某些东西的“官方”方法,但是我们可以使用一种技巧:

internal functions are not visible for Kotlin users, but it is visible from Java. There are some caveats though:

internal功能对Kotlin用户不可见,但是从Java中可见。 但是有一些警告:

  • This is a hack!

    这是骇客!

  • Interfaces can’t have internal members

    接口不能有internal成员

  • We need to use @JvmName because internal functions have a funky name when we try to access them from Java

    我们需要使用@JvmName因为当我们尝试从Java访问internal函数时, internal函数的名称很时髦

可扩展性 (Extensibility)

If you work on a library chances are that you want to design it for extensibility so your users can add their custom things. Take this interface for example, which we want to make extensible:

如果您使用的是库,则可能要设计其可扩展性,以便用户可以添加其自定义内容。 以这个接口为例,我们要使其具有可扩展性:

The problem here is that from the Java side calculateArea won’t have a default implementation only if we apply @JvmDefault to it. The problem is that this will only work with Java 8+ which might not be available (on Android for example).

这里的问题是,从Java角度@JvmDefault ,仅当我们将@JvmDefault应用于它时, calculateArea才会具有默认实现。 问题在于,这仅适用于可能不可用的Java 8+(例如,在Android上)。

So what we can do is to create base classes.

因此,我们可以做的是创建基类

If we want a base class which doesn’t implement all members we can provide abstract classes:

如果我们想要一个不能实现所有成员的基类,则可以提供abstract类:

If they do, an open class will do:

如果他们这样做,则open课将这样做:

Just keep those functions final which you don’t want your users to override.

只要保持这些功能final ,你不想让你的用户覆盖。

多平台注释 (Multiplatform Annotations)

Kotlin comes with some annotations which were designed to help with multiplatform development. One of them is @JvmStatic which we can use to make members static in the resulting Java bytecode, but it comes with some caveats:

Kotlin附带了一些注释,这些注释旨在帮助进行多平台开发。 其中之一是@JvmStatic ,我们可以使用它来使成员在生成的Java字节码中变为static ,但有一些警告:

Note that in the past this was not usable in common projects but they were modified to be optional so we can now put them on any class regardless of the platform.

请注意,过去这在普通项目中 不可用, 但是将它们修改为可选的, 因此我们现在可以将它们放在任何类上,而与平台无关。

One of those problems is that we can’t use it in interfaces, not even on companion objects defined in them.

这些问题之一是我们不能在interface使用它,甚至不能在它们中定义的companion objects上使用它。

A solution for this is to use objects and have them delegate to the functions defined in an interface companion object:

一种解决方案是使用object ,并将它们委托给interface companion object定义的功能:

多平台SAM问题 (Multiplatform SAM Problem)

Suppose that we have an interface which has a function which takes a lambda:

假设我们有一个interface ,该interface具有一个接受lambda的函数:

If we want to use this from the Java side it is awkward:

如果要从Java方面使用它,则很尴尬:

If we create a Listener interface to be used as a parameter:

如果我们创建一个Listener接口用作参数:

@FunctionalInterface would help here but we can’t use it in multiplatform common projects.

@FunctionalInterface在这里会有所帮助,但我们不能在多平台通用项目中使用它。

we won’t be able to use Kotlin lambdas here:

我们将无法在此处使用Kotlin lambda:

A solution for this problem is to keep the Listener interface and provide Kotlin users with an extension function which accepts a lambda:

解决此问题的方法是保留Listener界面,并为Kotlin用户提供接受lambda的扩展功能:

This way it will be idiomatic from both Java and Kotlin.

这样,Java和Kotlin都会习惯使用它。

如何部署? (How to Deploy?)

So we now have a nice library which is idiomatic, easy to maintain and behaves well in multiplatform environments. The question is how to deploy it? As of the time of writing, Maven Central and Bintray is hard to set up and the latter is not reliable. So what do we do?

因此,我们现在有了一个不错的库,该库是惯用的,易于维护的并且在多平台环境中表现良好。 问题是如何部署它? 在撰写本文时,Maven Central和Bintray很难设置,而后者并不可靠 。 那么我们该怎么办?

As it turns out there is a free service which works out of the box and deployment is as easy as creating a tag on GitHub: JitPack.

事实证明,有一项免费服务可以直接使用,并且部署就像在GitHub上创建标签一样简单: JitPack 。

My suggestion is to use this until official tooling arrives for Maven Central releases.

我的建议是使用此工具,直到Maven Central版本的官方工具到来为止。

Note that I’m not affiliated with JitPack in any way.

请注意,我不以任何方式隶属于JitPack。

结论 (Conclusion)

We’ve explored some of the intricacies of library development with Kotlin. It might seem hard to do at first, but by following some simple guidelines it can become much easier with some practice. So armed with this knowledge…

我们探索了Kotlin开发图书馆的一些复杂之处。 起初似乎很难做到,但是通过遵循一些简单的准则,可以通过一些实践变得更加容易。 所以有了这些知识...

Let’s go forth and kode on!

让我们去和KODE上

Thanks for reading! You can read more of my articles on my blog.

谢谢阅读! 您可以在我的博客上我的文章。

翻译自: https://www.freecodecamp.org/news/tips-for-writing-a-library-in-kotlin-cd5f9e14e102/

kotlin编写后台

kotlin编写后台_在Kotlin编写图书馆的提示相关推荐

  1. python编写代码_用 Python 编写干净、可测试、高质量的代码

    用 Python 编写干净.可测试.高质量的代码 Noah Gift 2010 年 12 月 20 日发布 简介 编写软件是人所承担的最复杂的任务之一.AWK 编程语言和 "K and R ...

  2. kotlin 构建对象_使用Kotlin,TypeScript和Okta构建安全的Notes应用程序

    kotlin 构建对象 I love my job as a developer advocate at Okta. I get to learn a lot, write interesting b ...

  3. kotlin协程_使Kotlin协程无缝采用的5个技巧

    kotlin协程 After successfully adopting coroutines in my prod project I think it is time to share 5 tip ...

  4. java编写存钱_用Java编写一个简单的存款

    package desposit.money; public class DespositMoney { public static void main(String[] args) { Custom ...

  5. 测试案例6种编写方法_一种编写测试的好方法

    测试案例6种编写方法 测试. 我最近一直在考虑测试. 作为我对各种项目所做的代码审查的一部分,我已经看到了数千行未经测试的代码. 这不仅是测试覆盖率统计数据指出这一点的情况,更是该项目中根本没有任何测 ...

  6. java编写管理系统_用java编写学生信息管理系统

    <用java编写学生信息管理系统>由会员分享,可在线阅读,更多相关<用java编写学生信息管理系统(7页珍藏版)>请在人人文库网上搜索. 1.用java编写学生信息管理系统im ...

  7. kotlin mysql数据库_在kotlin中使用mysql行级锁

    mysql中的锁 首先需要介绍一下mysql的锁.一般我们使用InnoDB数据库引擎+行级锁,SQL为:SELECT * FROM table where id = 1 for update;.for ...

  8. 编写代码的软件用什么编写的_如果您编写代码,这就是您的黄金时代

    编写代码的软件用什么编写的 这是10月22日至23日在北卡罗来纳州罗利举行的万物公开会议第一天的两个主题演讲的部分抄写. Forrester Research副总裁兼首席分析师Jeffrey Hamm ...

  9. python编写登录_通过Python编写一个简单登录功能过程解析

    通过Python编写一个简单登录功能过程解析 需求: 写一个登录的程序, 1.最多登陆失败3次 2.登录成功,提示欢迎xx登录,今天的日期是xxx,程序结束 3.要检验输入是否为空,账号和密码不能为空 ...

最新文章

  1. 鸿蒙推送升级包,华为鸿蒙系统已陆续推送!安卓可无缝升级,升级包容量高达6GB...
  2. 山东计算机编程哪个学校好,山东男孩,8岁懂电脑编程,10岁考上南科大,最后为何惨遭退学?...
  3. Java基础--二维数组
  4. PHP ----MySQL 数据库
  5. 高德地图天气图标符号大全_共享雨伞,高德这波营销格外暖!
  6. 数据库 文件 备份【学习 使用】
  7. VTK:非结构化网格之ClipUnstructuredGridWithPlane2
  8. (三)Linux查看和修改文件权限
  9. 十六进制,输出的时候怎样控制所输出字母的大小写。
  10. java vector_Java Vector elements()方法与示例
  11. delete响应服务器,rest-RESTful-DELETE响应主体应包含什么
  12. 可以提高千倍效率的Java代码小技巧
  13. Java异常框架设计
  14. Red5java.util.concurrent.RejectedExecutionExceptio
  15. 微信APP支付-Android+springboot搭建后端(一)
  16. PC硬件设备配置介绍与选型参考
  17. 视频文件常见格式-MP4
  18. 可以下载全球气象资料的网站
  19. Go语言开发第1课-环境搭建及简单程序入门
  20. 根据HSV颜色空间识别魔方是否还原

热门文章

  1. 【大牛疯狂教学】mysqlinnodb和myisam
  2. springboot-添加拦截器
  3. ES6入门之Generator函数
  4. python会什么比c慢
  5. [程序设计语言] 堆和栈的全面总结
  6. Bash:字符串操作
  7. Duplicate standby database from active database
  8. 使用DPAPI加密或解密你的数据
  9. ElasticSearch、Logstash和Kiabana三个开源工具。
  10. 数据库建表赋予权限语句