{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## 模拟场景渲染过程中驱动actor和绘图actor的信息交互过程" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "本例将表示几何图形的一组类的实例发送给actor,该actor将这组实例绘制到显示屏上。像是渲染工厂(rendering farm)在为动画生成场景。一旦场景渲染完毕,构成的场景的几何图形便会被发送给某一actor进行展示。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass \u001b[36mPoint\u001b[0m\n", "defined \u001b[32mclass \u001b[36mShape\u001b[0m\n", "defined \u001b[32mclass \u001b[36mCircle\u001b[0m\n", "defined \u001b[32mclass \u001b[36mRectangle\u001b[0m\n", "defined \u001b[32mclass \u001b[36mTriangle\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "case class Point(x: Double = 0.0, y: Double = 0.0)\n", "\n", "abstract class Shape() {\n", " // draw方法接受一个函数参数\n", " // 每个图形对象都会将自己的字符格式传给函数f,由f完成绘制工作\n", " def draw(f: String => Unit): Unit = f(s\"draw: ${this.toString}\")\n", "}\n", "\n", "case class Circle(center: Point, radius: Double) extends Shape\n", "case class Rectangle(lowerLeft: Point, height: Double, width: Double) extends Shape\n", "case class Triangle(point1: Point, point2: Point, point3: Point) extends Shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "样本类(case class)让编译器自动生成很多方法,比如String、equals和hashCode方法。编译器还会为样本类同时生成一个伴生对象。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mp20\u001b[0m: \u001b[32mPoint\u001b[0m = \u001b[33mPoint\u001b[0m(\u001b[32m2.0\u001b[0m, \u001b[32m0.0\u001b[0m)\n", "\u001b[36mp20b\u001b[0m: \u001b[32mPoint\u001b[0m = \u001b[33mPoint\u001b[0m(\u001b[32m2.0\u001b[0m, \u001b[32m0.0\u001b[0m)\n", "\u001b[36mp02\u001b[0m: \u001b[32mPoint\u001b[0m = \u001b[33mPoint\u001b[0m(\u001b[32m0.0\u001b[0m, \u001b[32m2.0\u001b[0m)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Scala调用生成的equals方法来判断==这样的逻辑比较\n", "val p20 = new Point(2.0)\n", "val p20b = new Point(2.0)\n", "val p02 = new Point(y = 2.0)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres2\u001b[0m: \u001b[32mBoolean\u001b[0m = false" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p20 == p02" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres3\u001b[0m: \u001b[32mBoolean\u001b[0m = true" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "p20 == p20b" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[36mp1\u001b[0m: \u001b[32mPoint\u001b[0m = \u001b[33mPoint\u001b[0m(\u001b[32m1.0\u001b[0m, \u001b[32m2.0\u001b[0m)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// 使用Point伴生类的apply方法\n", "\n", "val p1 = Point.apply(1.0, 2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Point.apply是构造Point对象的工厂方法,调用该方法就好像不通过new关键字调用Point的构造器一样。\n", "``` scala\n", "object Point {\n", " def apply(x: Double = 0.0, y: Double = 0.0) = new Point(x, y)\n", " ...\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "以上定义好了形状类型,接下来回到actor上" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject \u001b[36mMessages\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "object Messages {\n", " object Exit\n", " object Finished\n", " case class Response(message: String)\n", "}" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "load.jar(\"D:\\\\scala\\\\lib\\\\akka-actor_2.11-2.3.10.jar\")" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[32mimport \u001b[36makka.actor.Actor\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import akka.actor.Actor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "此处定义了一个actor类,用于绘制图形。实现了抽象方法Actor.receive,该方法是Actor的子类必须实现的方法,定义了如何处理接收到的消息。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass \u001b[36mShapesDrawingActor\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "class ShapesDrawingActor extends Actor {\n", " import Messages._\n", " \n", " def receive = {\n", " case s: Shape =>\n", " s.draw(str => println(s\"ShapesDrawingActor: $str\"))\n", " sender ! Response(s\"ShapesDrawingActor: $s drawn\")\n", " case Exit =>\n", " println(s\"ShapesDrawingActor: exiting...\")\n", " sender ! Finished\n", " case unexpected => //default. Equivalent to \"unexpected: Any\"\n", " val response = Response(s\"Error: Unknown message: $unexpected\")\n", " println(s\"ShapesDrawingActor: $response\")\n", " sender ! response\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "receive是偏函数,接受单一的Any类型参数并返回Unit值。由于该函数返回Unit对象,因此函数体一定会产生副作用。由于actor系统采用了异步消息机制,它必须依靠副作用。通常由于传递消息后无法返回任何消息,代码块中便会发送一些其他消息,包括给发送者的返回消息。\n", "\n", "偏函数中仅包含了一些case子句,对传递给函数的消息执行模型匹配。receive方法会尝试将接受到的各条消息与编写的模式匹配表达式进行匹配,并执行最先被匹配上的表达式。\n", "\n", "Actor.sender函数返回了actor发送消息接收方的对象引用,而!方法用于发送异步消息。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\u001b[32mimport \u001b[36makka.actor.{Props, ActorRef, ActorSystem}\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import akka.actor.{Props, ActorRef, ActorSystem}" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject \u001b[36mStart\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "object Start" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass \u001b[36mShapesDrawingDriver\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "class ShapesDrawingDriver(drawerActor: ActorRef) extends Actor {\n", " import Messages._\n", " \n", " def receive = {\n", " case Start =>\n", " drawerActor ! Circle(Point(0.0, 0.0), 1.0)\n", " drawerActor ! Rectangle(Point(0.0, 0.0), 2, 5)\n", " drawerActor ! 3.14159\n", " drawerActor ! Triangle(Point(0.0, 0.0), Point(2.0, 0.0), Point(1.0, 2.0))\n", " drawerActor ! Exit\n", " case Finished =>\n", " println(s\"ShapesDrawingDriver: cleaning up...\")\n", " context.system.shutdown()\n", " case response: Response =>\n", " println(\"ShapesDrawingDriver: Response =\" + response)\n", " case unexpected =>\n", " println(\"ShapesDrawingDriver: ERROR: Receive an unexpected message =\" + unexpected)\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "接下来是驱动应用的主方法,首先构建一个akka.actor.ActorSystem对象和两个actor对象,然后将ShapesDrawingActor对象的引用传递给ShapesDrawingDriver,最后向驱动对象发送Start命令,启动应用。" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject \u001b[36mShapesDrawingApp\u001b[0m" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "object ShapesDrawingApp extends App{\n", " val system = ActorSystem(\"DrawingActorSystem\")\n", " val drawer = system.actorOf(Props(new ShapesDrawingActor), \"drawingActor\")\n", " val driver = system.actorOf(Props(new ShapesDrawingDriver(drawer)), \"drawingService\")\n", " driver ! Start\n", " system.shutdown\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最后结果如图:\n", "![](images/shapes_result.jpg)" ] } ], "metadata": { "kernelspec": { "display_name": "Scala 2.11", "language": "scala211", "name": "scala211" }, "language_info": { "codemirror_mode": "text/x-scala", "file_extension": ".scala", "mimetype": "text/x-scala", "name": "scala211", "pygments_lexer": "scala", "version": "2.11.6" } }, "nbformat": 4, "nbformat_minor": 0 }