Passing and returning user data - are interfaces the right approach?

I am writing code in a C # library to allow clustering in a (two-dimensional) data set - essentially splitting the data into groups or clusters. To be useful, a library must accept "shared" or "user" data, put it in and return cluster data.

To do this, I need to assume that each base point in the transmitted dataset has a two-dimensional vector associated with it (in my case Lat , Lng - I work with coordinates).

My first thought was to use common types and pass in two lists one list of common data (i.e. List<T> ) and another of the same length defining 2D vectors (i.e. List<Coordinate> , where Coordinate is my class for specifying lat (lng) pairs, where the lists match each other by index. But this is rather tedious, because it means that in the algorithm I have to somehow track these indexes.

My next thought was to use inferfaces where I define the interface

 public interface IPoint { double Lat { get; set; } double Lng { get; set; } } 

and make sure that the data I transmit implements this interface (i.e. I can assume that each element passed has Lat and a Lng ).

But for me, this does not work either. I am using my C # library to stop clusters in a transit network (in another project). The class is called Stop , and this class is also from an external library, so I cannot implement an interface for this class.

What I did then was inherited from Stop by creating a class called ClusterableStop , which looks like this:

 public class ClusterableStop : GTFS.Entities.Stop, IPoint { public ClusterableStop(Stop stop) { Id = stop.Id; Code = stop.Code; Name = stop.Name; Description = stop.Description; Latitude = stop.Latitude; Longitude = stop.Longitude; Zone = stop.Zone; Url = stop.Url; LocationType = stop.LocationType; ParentStation = stop.ParentStation; Timezone = stop.Timezone; WheelchairBoarding = stop.WheelchairBoarding; } public double Lat { get { return this.Latitude; } } public double Lng { get { return this.Longitude; } } } 

which, as you can see, implements the IPoint interface. Now I use the constructor for ClusterableStop to first convert all Stop in the dataset to ClusterableStop s, then run the algorithm and get the result as ClusterableStop s.

This is not exactly what I want, because I want to do something in Stop based on which cluster they fall into. I canโ€™t do this because I actually created an instance of new stops, namely ClusterableStop !!

I can still achieve what I want, because, for example, I can restore the original objects by identifier. But of course, is there a much more elegant way to accomplish all this? Is it right to use interfaces? It seemed like such a simple idea - transitioning and returning user data - but it turned out to be so complicated.

+5
source share
2 answers

Have you considered using Tuples to do this work - this is sometimes a useful way to link two classes without creating an entire new class. You can create a list of tuples:

List<Tuple<Point, Stop>>

where Point is what you cluster on.

+2
source

Since you need to associate a pair (latitude, longitude) with each element of the 2D array, you can make a method that accepts a delegate that creates a linked position for each anchor point, for example:

 ClusterList Cluster<T>(IList<T> data, Func<int,Coordinate> getCoordinate) { for (int i = 0 ; i != data.Count ; i++) { T item = data[i]; Coordinate coord = getCoord(i); ... } } 

Now, before the caller, you can decide how the Coordinate connects to each data item.

Please note that association by list item is not the only option available to you. Another option is to pass in the delegate that takes the item and returns its coordinate:

 ClusterList Cluster<T>(IEnumerable<T> data, Func<T,Coordinate> getCoordinate) { foreach (var item in data) { Coordinate coord = getCoord(item); ... } } 

Although this approach is better than the index approach, in cases where the coordinates are not accessible to the object itself, this requires that the caller save some associative container on T , which should either play well with hash-based containers, or be IComparable<T> . In the first approach, there are no restrictions on T

In your case, the second approach is preferable:

 var clustered = Cluster( myListOfStops , stop => new Coordinate(stop.Latitude, stop.Longitude) ); 
+3
source

Source: https://habr.com/ru/post/1262361/


All Articles