2

Any idea how I can avoid these repeated class parameters?

abstract class XClass(  var x:  Int, var y:  Int, var w:  Int, var h:  Int) {...}
abstract class MXClass( var xS: Int, var yS: Int, var wS: Int, var hS: Int) 
                        extends XClass (xS, yS, wS, hS)
class PXClass( var xP: Int = 0, var yP: Int = 0, var wP: Int = 0, var hP: Int = 0) 
                extends MXClass (xP, yP, wP, hP)
def draw(g: Graphics2D) {
g fillRect (x, y, w, h)
}

then I have something like this in a GUI:

  g setColor black
  val cc = new PXClass(200, 300, 10, 10)
  cc.draw(g)

So I am giving those parameters as they are needed in the GUI!

Thanks

203
  • 499
  • 1
  • 6
  • 17
Valerin
  • 465
  • 3
  • 19
  • 1
    Could you tell us what you're trying to build here? – Erik Kaplun Oct 28 '14 at 19:22
  • the XClass is a `Piece` class and MXClass is a `MovablePiece` then PXClass is `RectanglePiece` class. Then I need `Piece` class to extend by `StationaryPiece`, thus I am trying to have some movable and statinary pieces in a BoardPanel of different shapes. – Valerin Oct 28 '14 at 19:34
  • What do all the type and val names stand for? – Erik Kaplun Oct 28 '14 at 19:36
  • they are the coordinates that I may need to build movable and not movable pieces and no mater of shapes ! – Valerin Oct 28 '14 at 19:37
  • they all have x and y coords and w and h? – Erik Kaplun Oct 28 '14 at 19:51
  • yes, because they are geometrical pieces: rectangle or an oval and even if it's an image I should put in a rectangle format or Oval one. – Valerin Oct 28 '14 at 19:53
  • You may need to know why I need from the beginning the coordinates, but I have in the parent Class, in `Piece` two methods about `collision` and `oleverlaps` and I thing it's necessary for them to be implemented in the parent Class because they are for both valid `movable` and `notMovable` pieces – Valerin Oct 28 '14 at 19:56

2 Answers2

5

According to what you said:

the XClass is a Piece class and MXClass is a MovablePiece then PXClass is RectanglePiece class. Then I need Piece class to extend by StationaryPiece, thus I am trying to have some movable and statinary pieces in a BoardPanel of different shapes. – Val 18 mins ago

Let's just start out by defining them one by one — let's not use any inheritance because it's not immediately obvious it's even needed; you can come back to inheritance (or some other type of polymorphism) when needed but trust me it's never actually needed:

sealed trait                  Shape
case object Rectangle extends Shape
case object Square    extends Shape
case object Circle    extends Shape

case class Dimensions(x: Int, y: Int, w: Int, h: Int)

case class Piece(shape: Shape, dim: Dimensions, isMovable: Boolean = true)

val squarePiece =        Piece(Square,    Dimensions(x = 100, y =. 200, w = 20, h = 20))
val circularPiece =      Piece(Circular,  Dimensions(x = 0,   y = -100, w = 40, h = 40))
val immovableRectPiece = Piece(Rectangle, Dimensions(...), isMovable = false)

and then you can define a function to take any piece and draw it:

def draw(piece: Piece) = piece.shape match {
  case Rectangle => ...
  case Square =>    ...
  case Circle =>    ...
}

...and methods for binary functions:

case class Piece(...) {
  def collidesWith(other: Piece) = ...
  def overlapsWith(other: Piece) = ...
}

then:

draw(immovablePiece)

if (piece1 collidesWith piece2) ...

if (piece1 overlapsWith piece2) ...

You're free to make draw a method of Piece as well, but it doesn't really matter — if you manage to avoid inheritance, all methods become equivalent to plain functions anyway.

There is no need to use inheritance (i.e. subtype polymorphism); it's always best to start out with mathematically purer concepts and avoid extends altogether. If you consider the code I've demonstrated, you'll realise that it gives you a way to compose bits and pieces instead of providing monolithic types; it also allows you to convert one type of piece to another by providing an alternative value for an attribute — this is not possible with inheritance; it's not possible to change the type of an object once created.

val immovableCirclePiece = circularPiece.copy(isMovable = false)

Note that the Shape inheritance tree starting with that sealed trait is not the same as regular inheritance — what it does it defines an Algebraic Data Type, which in this case resembles quite closely what would be an enum in Java.


Furthermore, you could try to extract the data that is relevant to drawing/rendering and put it all in a, say, Representation class that holds both the Point as well as a rendering function to draw specific shapes. Feel free to ask for clarification — there's a lot to say and I'm only trying to get you started with designing your programs in a more sophisticated manner :)

Community
  • 1
  • 1
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • it's so interesting your reply, and I have design my program more as in OO Java and still I am mixing the Java and simplifications in Scala. Already I have so much code, and it's functional (but not so nice for the eyes) I will try first to find if I can use with less effort your proposal, then let see if I have further questions. – Valerin Oct 28 '14 at 20:21
  • Yes, getting away from the Java OO mindset that mainly focusses around inheritance (i.e. subtype polymorphism) is exactly the point of my answer — I'm glad it got thru! – Erik Kaplun Oct 28 '14 at 20:41
  • Heyy, I have combine partly your suggestion and I am so exited, now the whole packages are looking better :D Thnxx – Valerin Oct 28 '14 at 22:17
  • One thing I missed in your post: you're using `var`s — you shouldn't by default; use `val`s (which is the default with case classes if you don't specify anything). – Erik Kaplun Oct 28 '14 at 23:31
  • I am using `val` whenever is possible but for example in a such case class `case class Dimensions(x: Int, y: Int, w: Int, h: Int)` the parameters are needed to be `var`-s because the position `x and y` of the `shapes` get changed during the time :( Thnxx a lot! – Valerin Oct 29 '14 at 09:05
  • Still use a `val` -- you can use `foo.copy(x = newvalue)` instead of `foo.x = newvalue`; if your code design doesn't allow for that, redesign your code so that it would; basically this boils down to functional design. – Erik Kaplun Oct 29 '14 at 12:17
  • in my case, because I have a `move()` method that change the coordinates if `pieces` are supposed to move then using `copy` method is making my code hardly readable :(, it sounds like a trade-off in this case, using `var`-s or `copy()` method. And it's not my intention to make the code "pure functional" even working with `val`s I feel more comfortable. – Valerin Oct 29 '14 at 13:18
-2

Just write:

class PXClass extends MXClass (0, 0, 0, 0) 
Anatoliy Kmetyuk
  • 708
  • 1
  • 6
  • 10
  • I just gave more details in my question! I can left without initiating those parameters but it's so boring seeing them repeated from class to class :(, thus I just initiated them with default values. – Valerin Oct 28 '14 at 19:21
  • Have I understood you correctly: you want to define the constructor parameters only for the parent class and let the child class reuse them? – Anatoliy Kmetyuk Oct 28 '14 at 19:23
  • I have some general methods (in parent class) that need these parameters in the XClass - parent class and then going further when I construct the childClases I am using those methods but I need to set these parameters with concrete values (like in my case in a GUI class) – Valerin Oct 28 '14 at 19:29
  • What about using traits then? Instead of defining `class PXClass(...) extends XClass(...)`, you can define `trait PXClass {this: XClass => ...` that contains what `PXClass` was supposed to contain. Then, when you need a `PXClass` instance, simply write `new XClass(...) with PXClass`. – Anatoliy Kmetyuk Oct 28 '14 at 19:42
  • @AnatoliyKmetyuk let see if I will get your explanation, I will try now if it's functional in my case! – Valerin Oct 28 '14 at 19:51
  • 1
    @AnatoliyKmetyuk: you'll only ever make things more complicated by introducing more and trickier inheritance. – Erik Kaplun Oct 28 '14 at 22:22
  • @ErikAllik, I agree, your solution is more intuitive. Traits would be an overhead here. – Anatoliy Kmetyuk Oct 28 '14 at 22:26