HoTicE
HoTicE

Reputation: 573

Best way to run common function from different classes

I have this classes

public abstract class Foo {
    def execute: Unit = ???
}

public abstract class Bar {
    def execute: Unit = ???
}

public class FooFoo extends Foo {
    def execute: Unit = ???
}

public class BarBar extends Bar {
    def execute: Unit = ???
}

In some where I have this method:

def executeSomething(body: => AnyRef) : = Try(body) match ...

And the calls look like this

x match {
  case _: Foo => executeSomething(x.execute)
  case _: Bar => executeSomething(x.execute)
}

Is there are some way then I can do that like this (without new classes )

val u = executeSomething(x)

?

upd

sorry, guys. This m.b. real code will be more clear

import akka.actor.{Actor, ActorLogging, ActorRef, Props}
import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest
import com.google.api.client.googleapis.json.GoogleJsonResponseException
import com.google.api.services.analytics.Analytics
import com.google.api.services.analyticsreporting.v4.AnalyticsReporting
import com.my.lab.messages.{GRequest, GResponse}
import scala.util.{Failure, Success, Try}

class GQueueTask(req: GRequest, ref: ActorRef) extends Actor with ActorLogging {

  def receive: Receive = {
    case _ =>
  }

  def execute(body: => AnyRef): Unit = {
    Try(body) match {
      case Success(r) => ref ! GResponse(req, response = Option(r))
      case Failure(f: GoogleJsonResponseException) =>
            f.printStackTrace()
            ref ! GResponse(req, error = Option(f))
      case _ => ref ! GResponse(req, Option("unknown error"))
    }
  }

  req match {
    case GRequest(request, _) => request match {
      case x: AnalyticsReporting#Reports#BatchGet => execute(x.execute()) // AbstractGoogleClientRequest
      case x: Analytics#Data#Ga#Get => execute(x.execute()) // AbstractGoogleClientRequest
      case x: Analytics#Management#Accounts#List => execute(x.execute()) // AbstractGoogleClientRequest
      case x: Analytics#Management#Webproperties#List => execute(x.execute()) // AbstractGoogleClientRequest
      case x: Analytics#Management#Profiles#List => execute(x.execute()) // AbstractGoogleClientRequest
      case x: AuthorizationCodeTokenRequest => execute(x.execute()) // TokenRequest
      case _ => ref ! GResponse(req)
    }
  }

  context stop self
}

I tried to simplify "request match" block

Upvotes: 2

Views: 328

Answers (3)

sarveshseri
sarveshseri

Reputation: 13985

This looks like a pretty good use-case for duck-typing.

Duck Typing is more formally referred as Structural Types.

A Duck Type can be thought of as just a specification of desired set of properties. Compile time reflection is used to ensure that the type of provided parameter is compatible with the specification of your duck-type. So it provides you compile-time type-safety.

And then it uses run time reflection to actually call the respective methods on provided instance.

So,

Lets say you have following abstractions,

abstract class Foo {
  def execute(): Unit
}

abstract class Bar {
  def execute(): Unit
}

class FooFoo extends Foo {
  override def execute(): Unit = {
    println("FooFoo")
  }
}

class BarBar extends Bar {
  override def execute(): Unit = {
    println("BarBar")
  }
}

Note :: I have added () to all your Unit methods. This is a convention in Scala that all methods with side-effects should have (). And a Unit method can not do anything other than side effects.

Now... you just need to define a duck-type for the shared behaviour,

type HasExecuteMethod = {
  def execute(): Unit
}

Now... lets define a function which uses this duck-type,

def doExecute(hasExecuteMethod: HasExecuteMethod): Unit = {
  hasExecuteMethod.execute()
}

And... here you have it. This doExecute method will accept instance of any class which has a def execute(): Unit member.

Upvotes: 3

NetanelRabinowitz
NetanelRabinowitz

Reputation: 1594

Although you can achieve it by using structural typing (duck typing), it has a lot of overhead since scala use introspection at runtime. I think The best way to achieve it is by using type classes. It's in use in Scala all over the place :) Scala's Ordering is one example.

A type class defines functionality associated with "unrelated" types. What you need to do is define the type class in the form of a trait:

trait Executable[T] {
    def execute(t: T): Unit
}

Than you need to make your types a member of that type class, for example:

implicit object FooFooExecutable extends Executable[FooFoo] {
  override def execute(t: FooFoo): Unit = t.execute()
}

And the last thing is to define the method that takes your type class as an argument.

def exec[T: Executable](t: T): Unit = {
  implicitly[ExecTypeClass[T]].execute(t)
}

You can read more about type classes here, here and here

Upvotes: 6

Sami Badawi
Sami Badawi

Reputation: 1032

I tried this and it worked:

object Dummy {
  abstract class Foo {
    def execute: Unit
  }

  abstract class Bar {
    def execute: Unit
  }

  class FooFoo extends Foo {
    def execute: Unit = { println("Foo") }
  }

  class BarBar extends Bar {
    def execute: Unit = { println("Bar") }
  }

  def run(x: AnyRef): Unit = {

    x match {
      case f: Foo => f.execute
      case b: Bar => b.execute
      case _ =>
    }
  }

  def main(args: Array[String]): Unit = {
    val any = new BarBar()
    run(any)
  }
}

It printed

Bar

Upvotes: 1

Related Questions