I suspect there's a Pattern for This: Optional Parameters vs. Overloading
-
I did have a bunch of similar public methods for converting an RGB structure to grey scale but decided it made the class too complicated so I changed them to private and created an Enum which was used as a parameter for a public method which then called those private methods method using switch. One of the private methods then developed a need for a parameter, but only one of them. I made the parameter optional but then stumbled into the problem of public shared methods not being considered as constants, so I switched to overloading for this method and wanted to add an optional Enum to represent the two variants of that one method, this smelt bad. So I thought to use overloading again, but the second version of the method would still be able to accept all the options of the first Enum, even though only one was relevant, the one that needed the second Enum, still smelt bad. I'm struggling to follow this explanation myself, so if code helps then here it is (simplified). I don't like it. I could possibly use the second ToGreyScale method with just the LumaEnum parameter but that, to my mind, makes the use of the class problematic since it's not intuitive. Calling the second method GreyScalebyLuminosity makes a nonsense of trying to get rid of all those differently named GreyScale methods. Is there a pattern I can use here? Or just a better design than this?
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod)
Select Case method
Case RGBGreyScaleMethod.Average
Me.GreyScaleByAverage()
Case RGBGreyScaleMethod.BlueChannel
Me.GreyScaleFromBlue()
Case RGBGreyScaleMethod.Decompose
Me.Decompose()
Case RGBGreyScaleMethod.Luminosity
Me.GreyScaleByLuminosity()
Case Else
Throw New ArgumentOutOfRangeException()
End Select
End SubPublic Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
Select Case method
Case RGBGreyScaleMethod.Luminosity
Select Case factors
Case LumaEnum.BT709
Me.GreyScaleByLuminosity(LumaFactors.BT709)
Case LumaEnum.BT601
Me.GreyScaleByLuminosity(LumaFactors.BT601)
Case LumaEnum.GIMP
Me.GreyScaleByLuminosity(LumaFactors.GIMP)
Case Else
Throw New ArgumentOutOfRangeException()
End Select
Case Else
Thr -
I did have a bunch of similar public methods for converting an RGB structure to grey scale but decided it made the class too complicated so I changed them to private and created an Enum which was used as a parameter for a public method which then called those private methods method using switch. One of the private methods then developed a need for a parameter, but only one of them. I made the parameter optional but then stumbled into the problem of public shared methods not being considered as constants, so I switched to overloading for this method and wanted to add an optional Enum to represent the two variants of that one method, this smelt bad. So I thought to use overloading again, but the second version of the method would still be able to accept all the options of the first Enum, even though only one was relevant, the one that needed the second Enum, still smelt bad. I'm struggling to follow this explanation myself, so if code helps then here it is (simplified). I don't like it. I could possibly use the second ToGreyScale method with just the LumaEnum parameter but that, to my mind, makes the use of the class problematic since it's not intuitive. Calling the second method GreyScalebyLuminosity makes a nonsense of trying to get rid of all those differently named GreyScale methods. Is there a pattern I can use here? Or just a better design than this?
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod)
Select Case method
Case RGBGreyScaleMethod.Average
Me.GreyScaleByAverage()
Case RGBGreyScaleMethod.BlueChannel
Me.GreyScaleFromBlue()
Case RGBGreyScaleMethod.Decompose
Me.Decompose()
Case RGBGreyScaleMethod.Luminosity
Me.GreyScaleByLuminosity()
Case Else
Throw New ArgumentOutOfRangeException()
End Select
End SubPublic Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
Select Case method
Case RGBGreyScaleMethod.Luminosity
Select Case factors
Case LumaEnum.BT709
Me.GreyScaleByLuminosity(LumaFactors.BT709)
Case LumaEnum.BT601
Me.GreyScaleByLuminosity(LumaFactors.BT601)
Case LumaEnum.GIMP
Me.GreyScaleByLuminosity(LumaFactors.GIMP)
Case Else
Throw New ArgumentOutOfRangeException()
End Select
Case Else
ThrMike-MadBadger wrote:
I'm struggling to follow this explanation myself
I tried twice, and got lost twice. Let's try it differently; you've seen how the different events in .NET all look similar, with two parameters? One could do something similar;
Public Class MyParamaterBase
Public Property Id As Guid
End ClassPublic Class MyExtendedParameter
Public Property Title as String
End ClassPublic Class SomeConsumingClass
Public Sub SomeMethod(TheParameter As MyParameterBase)
' to get to the "Title" property
If TheParameter is MyExtendedParameter Then
Console.WriteLine((TheParameter as MyExtendedParameter).Title)
EndIf
End SubEnd Class
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]
-
I did have a bunch of similar public methods for converting an RGB structure to grey scale but decided it made the class too complicated so I changed them to private and created an Enum which was used as a parameter for a public method which then called those private methods method using switch. One of the private methods then developed a need for a parameter, but only one of them. I made the parameter optional but then stumbled into the problem of public shared methods not being considered as constants, so I switched to overloading for this method and wanted to add an optional Enum to represent the two variants of that one method, this smelt bad. So I thought to use overloading again, but the second version of the method would still be able to accept all the options of the first Enum, even though only one was relevant, the one that needed the second Enum, still smelt bad. I'm struggling to follow this explanation myself, so if code helps then here it is (simplified). I don't like it. I could possibly use the second ToGreyScale method with just the LumaEnum parameter but that, to my mind, makes the use of the class problematic since it's not intuitive. Calling the second method GreyScalebyLuminosity makes a nonsense of trying to get rid of all those differently named GreyScale methods. Is there a pattern I can use here? Or just a better design than this?
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod)
Select Case method
Case RGBGreyScaleMethod.Average
Me.GreyScaleByAverage()
Case RGBGreyScaleMethod.BlueChannel
Me.GreyScaleFromBlue()
Case RGBGreyScaleMethod.Decompose
Me.Decompose()
Case RGBGreyScaleMethod.Luminosity
Me.GreyScaleByLuminosity()
Case Else
Throw New ArgumentOutOfRangeException()
End Select
End SubPublic Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
Select Case method
Case RGBGreyScaleMethod.Luminosity
Select Case factors
Case LumaEnum.BT709
Me.GreyScaleByLuminosity(LumaFactors.BT709)
Case LumaEnum.BT601
Me.GreyScaleByLuminosity(LumaFactors.BT601)
Case LumaEnum.GIMP
Me.GreyScaleByLuminosity(LumaFactors.GIMP)
Case Else
Throw New ArgumentOutOfRangeException()
End Select
Case Else
Thr -
Mike-MadBadger wrote:
I'm struggling to follow this explanation myself
I tried twice, and got lost twice. Let's try it differently; you've seen how the different events in .NET all look similar, with two parameters? One could do something similar;
Public Class MyParamaterBase
Public Property Id As Guid
End ClassPublic Class MyExtendedParameter
Public Property Title as String
End ClassPublic Class SomeConsumingClass
Public Sub SomeMethod(TheParameter As MyParameterBase)
' to get to the "Title" property
If TheParameter is MyExtendedParameter Then
Console.WriteLine((TheParameter as MyExtendedParameter).Title)
EndIf
End SubEnd Class
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]
-
Use a definition object. That is passed to the second object and defines how it runs. The definition object can provide things like a default definition, common definitions, common ways to create definitions and custom definitions.
-
Ok, so instead of an Enum parameter I have a base class parameter which through polymorphism can take a derived type which carries the extra parameter information? I guess I could do the same thing by using an interface as the parameter? Thanks, Mike
-
Mike-MadBadger wrote:
I'm struggling to follow this explanation myself
I tried twice, and got lost twice. Let's try it differently; you've seen how the different events in .NET all look similar, with two parameters? One could do something similar;
Public Class MyParamaterBase
Public Property Id As Guid
End ClassPublic Class MyExtendedParameter
Public Property Title as String
End ClassPublic Class SomeConsumingClass
Public Sub SomeMethod(TheParameter As MyParameterBase)
' to get to the "Title" property
If TheParameter is MyExtendedParameter Then
Console.WriteLine((TheParameter as MyExtendedParameter).Title)
EndIf
End SubEnd Class
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]
I think this is simply a variation on your suggestion (in effect), using a nested class.
Public Class Class1
Private \_param As Double Public Sub New(ByVal amount As Double) Me.\_param = amount End Sub Public Property Param() As Double Get Return Me.\_param End Get Set(value As Double) If Not value = Me.\_param Then Me.\_param = value End If End Set End Property Public Function ToGrey() As ToGreyMethods Return New ToGreyMethods(Me) End Function Public Class ToGreyMethods Private \_outer As Class1 Public Sub New(ByRef outer As Class1) Me.\_outer = outer End Sub Public Sub Increase(ByVal amount As Double) Me.\_outer.Param += amount End Sub Public Sub Decrease(ByVal amount As Double) Me.\_outer.Param -= amount End Sub End Class
End Class
Module Module1
Sub Main() Dim thing As Class1 = New Class1(10) thing.ToGrey.Increase(5) Console.WriteLine(thing.Param.ToString()) thing.ToGrey.Decrease(10) Console.WriteLine(thing.Param.ToString()) Console.ReadKey() End Sub
End Module
'Output is:
'15
'5My only problem with this is that I can't seem to hide the nested class so one could be created at runtime manually rather than as a result of a call to ToGrey(), which starts to feel a bit messy or at least has the potential to get messy since I do.t know what would happen if, for example, you could do this.
Public Sub Example()
Dim thing as Class1 = New Class1(10)
Dim whoops as ToGreyMethods = thing.ToGrey()
'I now have an instance of an object that I can start using to change the thing object without directly referring to thing, feels like a recipe for confusion...
End Sub -
Mike-MadBadger wrote:
I'm struggling to follow this explanation myself
I tried twice, and got lost twice. Let's try it differently; you've seen how the different events in .NET all look similar, with two parameters? One could do something similar;
Public Class MyParamaterBase
Public Property Id As Guid
End ClassPublic Class MyExtendedParameter
Public Property Title as String
End ClassPublic Class SomeConsumingClass
Public Sub SomeMethod(TheParameter As MyParameterBase)
' to get to the "Title" property
If TheParameter is MyExtendedParameter Then
Console.WriteLine((TheParameter as MyExtendedParameter).Title)
EndIf
End SubEnd Class
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]
-
I think this is simply a variation on your suggestion (in effect), using a nested class.
Public Class Class1
Private \_param As Double Public Sub New(ByVal amount As Double) Me.\_param = amount End Sub Public Property Param() As Double Get Return Me.\_param End Get Set(value As Double) If Not value = Me.\_param Then Me.\_param = value End If End Set End Property Public Function ToGrey() As ToGreyMethods Return New ToGreyMethods(Me) End Function Public Class ToGreyMethods Private \_outer As Class1 Public Sub New(ByRef outer As Class1) Me.\_outer = outer End Sub Public Sub Increase(ByVal amount As Double) Me.\_outer.Param += amount End Sub Public Sub Decrease(ByVal amount As Double) Me.\_outer.Param -= amount End Sub End Class
End Class
Module Module1
Sub Main() Dim thing As Class1 = New Class1(10) thing.ToGrey.Increase(5) Console.WriteLine(thing.Param.ToString()) thing.ToGrey.Decrease(10) Console.WriteLine(thing.Param.ToString()) Console.ReadKey() End Sub
End Module
'Output is:
'15
'5My only problem with this is that I can't seem to hide the nested class so one could be created at runtime manually rather than as a result of a call to ToGrey(), which starts to feel a bit messy or at least has the potential to get messy since I do.t know what would happen if, for example, you could do this.
Public Sub Example()
Dim thing as Class1 = New Class1(10)
Dim whoops as ToGreyMethods = thing.ToGrey()
'I now have an instance of an object that I can start using to change the thing object without directly referring to thing, feels like a recipe for confusion...
End SubMike-MadBadger wrote:
I now have an instance of an object that I can start using to change the thing object without directly referring to thing, feels like a recipe for confusion...
The effect would also be there without the new object; any object that's embedded in a property can be put in a variable, and one can manipulate the referenced object without looking at whose property it was.
Public Class Class1
Private \_param As Double Private \_methods As ToGreyMethods Public Sub New(ByVal amount As Double) Me.\_param = amount Me.\_methods = New ToGreyMethods(Me) End Sub Public Property Param() As Double Get Return Me.\_param End Get Set(ByVal value As Double) If Not value = Me.\_param Then Me.\_param = value End If End Set End Property Public ReadOnly Property ToGrey() As ToGreyMethods Get Return \_methods End Get End Property Public Class ToGreyMethods Private \_outer As Class1 Public Sub New(ByRef outer As Class1) Me.\_outer = outer End Sub Public Sub Increase(ByVal amount As Double) Me.\_outer.Param += amount End Sub Public Sub Decrease(ByVal amount As Double) Me.\_outer.Param -= amount End Sub End Class
End Class
Module Module1
Sub Main() Dim thing As Class1 = New Class1(10) thing.ToGrey.Increase(5) Console.WriteLine(thing.Param.ToString()) thing.ToGrey.Decrease(10) Console.WriteLine(thing.Param.ToString()) ' Dim whoops2 As Font = Form1.Font <- now you got the same thing :) Dim whoops As Class1.ToGreyMethods = thing.ToGrey() Console.ReadKey() End Sub
End Module
Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]
-
I did have a bunch of similar public methods for converting an RGB structure to grey scale but decided it made the class too complicated so I changed them to private and created an Enum which was used as a parameter for a public method which then called those private methods method using switch. One of the private methods then developed a need for a parameter, but only one of them. I made the parameter optional but then stumbled into the problem of public shared methods not being considered as constants, so I switched to overloading for this method and wanted to add an optional Enum to represent the two variants of that one method, this smelt bad. So I thought to use overloading again, but the second version of the method would still be able to accept all the options of the first Enum, even though only one was relevant, the one that needed the second Enum, still smelt bad. I'm struggling to follow this explanation myself, so if code helps then here it is (simplified). I don't like it. I could possibly use the second ToGreyScale method with just the LumaEnum parameter but that, to my mind, makes the use of the class problematic since it's not intuitive. Calling the second method GreyScalebyLuminosity makes a nonsense of trying to get rid of all those differently named GreyScale methods. Is there a pattern I can use here? Or just a better design than this?
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod)
Select Case method
Case RGBGreyScaleMethod.Average
Me.GreyScaleByAverage()
Case RGBGreyScaleMethod.BlueChannel
Me.GreyScaleFromBlue()
Case RGBGreyScaleMethod.Decompose
Me.Decompose()
Case RGBGreyScaleMethod.Luminosity
Me.GreyScaleByLuminosity()
Case Else
Throw New ArgumentOutOfRangeException()
End Select
End SubPublic Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
Select Case method
Case RGBGreyScaleMethod.Luminosity
Select Case factors
Case LumaEnum.BT709
Me.GreyScaleByLuminosity(LumaFactors.BT709)
Case LumaEnum.BT601
Me.GreyScaleByLuminosity(LumaFactors.BT601)
Case LumaEnum.GIMP
Me.GreyScaleByLuminosity(LumaFactors.GIMP)
Case Else
Throw New ArgumentOutOfRangeException()
End Select
Case Else
ThrIt looks like you found a solution that you are happy with, but I thought that I would throw my 2 cents worth into the fray. Based on my understanding of your code, the Luminosity method is the only one that needs a second parameter. Why not remove Luminosity from your methods enum and change:
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
to:Public Overloads Sub ToGreyScale(ByVal factors as LumaEnum)
I think that this would given you the intuitiveness factor that you are seeking. As I don't know what it all is that you are doing in this "Class" the following may not be relevant, but I will throw it out as an alternative. It appears that all you are using it for is to convert a Color structure to its equivalent greyscale color. Perhaps, this may be a case for using extension methods to the Color Structure.Module GreyScaleExtentions
Public Enum RBGMethods
Average
BlueChannel
Decompose
Desaturate
Lightness
RedChannel
GreenChannel
End EnumPublic Enum LumaEnum
BT709
BT601
GIMP
End Enum''' <summary>
''' Converts Color to greyscale equivalent using RGBGreyScaleMethod
''' </summary>
''' <param name="c"></param>
''' <param name="RGBMethod"></param>
''' <returns></returns>
''' <remarks></remarks>
<Runtime.CompilerServices.Extension()> _
Public Function ToGreyScale(ByVal c As Color, ByVal RGBMethod As RBGMethods) As ColorEnd Function
''' <summary>
''' Converts Color to greyscale equivalent using luminosity method
''' </summary>
''' <param name="c"></param>
''' <param name="factors"></param>
''' <returns></returns>
''' <remarks></remarks>
<Runtime.CompilerServices.Extension()> _
Public Function ToGreyScale(ByVal c As Color, ByVal factors As LumaEnum) As ColorEnd Function
End ModuleThen you could use like this:
Sub test()
Dim c As Color = Color.FromArgb(230, 134, 231, 10)
Dim gs As Color
gs = c.ToGreyScale(LumaEnum.BT601)
gs = c.ToGreyScale(RBGMethods.Av -
It looks like you found a solution that you are happy with, but I thought that I would throw my 2 cents worth into the fray. Based on my understanding of your code, the Luminosity method is the only one that needs a second parameter. Why not remove Luminosity from your methods enum and change:
Public Overloads Sub ToGreyScale(ByVal method As RGBGreyScaleMethod, ByVal factors as LumaEnum)
to:Public Overloads Sub ToGreyScale(ByVal factors as LumaEnum)
I think that this would given you the intuitiveness factor that you are seeking. As I don't know what it all is that you are doing in this "Class" the following may not be relevant, but I will throw it out as an alternative. It appears that all you are using it for is to convert a Color structure to its equivalent greyscale color. Perhaps, this may be a case for using extension methods to the Color Structure.Module GreyScaleExtentions
Public Enum RBGMethods
Average
BlueChannel
Decompose
Desaturate
Lightness
RedChannel
GreenChannel
End EnumPublic Enum LumaEnum
BT709
BT601
GIMP
End Enum''' <summary>
''' Converts Color to greyscale equivalent using RGBGreyScaleMethod
''' </summary>
''' <param name="c"></param>
''' <param name="RGBMethod"></param>
''' <returns></returns>
''' <remarks></remarks>
<Runtime.CompilerServices.Extension()> _
Public Function ToGreyScale(ByVal c As Color, ByVal RGBMethod As RBGMethods) As ColorEnd Function
''' <summary>
''' Converts Color to greyscale equivalent using luminosity method
''' </summary>
''' <param name="c"></param>
''' <param name="factors"></param>
''' <returns></returns>
''' <remarks></remarks>
<Runtime.CompilerServices.Extension()> _
Public Function ToGreyScale(ByVal c As Color, ByVal factors As LumaEnum) As ColorEnd Function
End ModuleThen you could use like this:
Sub test()
Dim c As Color = Color.FromArgb(230, 134, 231, 10)
Dim gs As Color
gs = c.ToGreyScale(LumaEnum.BT601)
gs = c.ToGreyScale(RBGMethods.AvThat one had occured but it meant that a user of the class would need to know that the overload without a method would give ByLuminosity, which was the bit that felt unintuitive. BTW, thanks for the piece, I assume that's how extension methods work? I might add those to extend the Color type to include the functionality in my RGB and aRGB types, thanks.
-
That one had occured but it meant that a user of the class would need to know that the overload without a method would give ByLuminosity, which was the bit that felt unintuitive. BTW, thanks for the piece, I assume that's how extension methods work? I might add those to extend the Color type to include the functionality in my RGB and aRGB types, thanks.
Quote:
That one had occured but it meant that a user of the class would need to know that the overload without a method would give ByLuminosity, which was the bit that felt unintuitive.
The issue of being intuitive is very subjective. I see the intuitive part being to use the "ToGreyScale" method and even that may require some explanation. At some point a programmer has to demonstrate some competence and look at the provided documentation. This is the reason that I showed the visual studio intellisense support using xml comments in the code I posted (see: Documenting Your Code With XML Comments). Providing this documentation is your responsibility as the developer and you should not be relying on something being intuitive as a substitute for proper documentation. Paste the extension method code I provided into a project and observe the intellisence pop-ups when you try to use the defined methods.
Quote:
BTW, thanks for the piece, I assume that's how extension methods work?
Assume nothing ;P when coding. Use your friendly search engine and research it yourself. I have found that this general query works well in the major search engines: msdn TopicYouWant -social" The reason for the "-social" is to try to eliminate results from the msdn help forums and to focus on the documentation pages. So much for my preaching for the day, have a good weekend. :-O
-
Unfortunately I'm not sure exactly what a definition object is and Google hasn't helped, could you possibly help me with a link or a bit more explanation?
-
Quote:
That one had occured but it meant that a user of the class would need to know that the overload without a method would give ByLuminosity, which was the bit that felt unintuitive.
The issue of being intuitive is very subjective. I see the intuitive part being to use the "ToGreyScale" method and even that may require some explanation. At some point a programmer has to demonstrate some competence and look at the provided documentation. This is the reason that I showed the visual studio intellisense support using xml comments in the code I posted (see: Documenting Your Code With XML Comments). Providing this documentation is your responsibility as the developer and you should not be relying on something being intuitive as a substitute for proper documentation. Paste the extension method code I provided into a project and observe the intellisence pop-ups when you try to use the defined methods.
Quote:
BTW, thanks for the piece, I assume that's how extension methods work?
Assume nothing ;P when coding. Use your friendly search engine and research it yourself. I have found that this general query works well in the major search engines: msdn TopicYouWant -social" The reason for the "-social" is to try to eliminate results from the msdn help forums and to focus on the documentation pages. So much for my preaching for the day, have a good weekend. :-O
Thank you. My code is XML commented I just left it out of the simplified examples here. For the extension methods I'm not sure why I left the question in, I had searched Google, guess I was just looking for confirmation - probably not necessary. On the structure, I agree but something feels clunky, nevermind. Have a nice weekend