When I'm developing, I usually try to follow the SOLID principles.
Usually you have an interface that all affected classes implement and then use the interface as a parameter when making further calculations.
My question is how can this be achieved when calling, for example, a web service? The code below is not very lean and does NOT indeed meet the Single Responsibility Pattern and the Open/Closed Principle.
How would you re-design the following code to follow SRP and O/C:
public class Fetch
{
public void Run()
{
var url = "https://api.nasa.gov/planetary/apod?api_key=NNKOjkoul8n1CH18TWA9gwngW1s1SmjESPjNoUFo";
var client = new HttpClient();
var response = client.GetAsync(url).Result;
var data = response.Content.ReadAsStringAsync().Result;
var parsedData = JsonConvert.DeserializeObject<Nasa>(data);
if(parsedData.media_type.Equals("image"))
{
CreateImage(parsedData);
}
if (parsedData.media_type.Equals("video"))
{
CreateVideo(parsedData);
}
if (parsedData.media_type.Equals("text"))
{
CreateText(parsedData);
}
}
}
public class Nasa
{
public string copyright { get; set; }
public string date { get; set; }
public string explanation { get; set; }
public string hdurl { get; set; }
public string media_type { get; set; }
public string service_version { get; set; }
public string title { get; set; }
public string url { get; set; }
}
(The Api key is taken from Nasa's example site so don't worry about exposing it. The async part using .Result is just for this example)
Without knowing much about your needs, or how the CreateVideo(item) and CreateImage(item) works, you can use this as a starting point.
Please note that if you are using a dependency container (like Simple Injector, love it!) much of the dependencies in the following can be provided by the container.
public class Nasa
{
public string copyright { get; set; }
public string date { get; set; }
public string explanation { get; set; }
public string hdurl { get; set; }
public string media_type { get; set; }
public string service_version { get; set; }
public string title { get; set; }
public string url { get; set; }
}
public interface ITransformFetch<in T>
{
void Transform(T data);
}
public interface IFetch<T>
{
T Fetch();
}
public class NasaFetcher : IFetch<Nasa>
{
private const string NasaUrl = "https://api.nasa.gov/planetary/apod?api_key=NNKOjkoul8n1CH18TWA9gwngW1s1SmjESPjNoUFo";
private readonly IHttpClientWrapper _client;
public NasaFetcher(IHttpClientWrapper client)
{
_clientFactory = client;
}
public Nasa Fetch()
{
var response = _client.GetAsync(NasaUrl).Result;
var data = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<Nasa>(data);
}
}
public class NasaFetchImageTransformer : ITransformFetch<Nasa>
{
public void Transform(Nasa data)
{
// transform data
}
}
public class NasaFetchVideoTransformer : ITransformFetch<Nasa>
{
public void Transform(Nasa data)
{
// transform data
}
}
public class NasaFetcherTransformerDecorator : IFetch<Nasa>
{
private readonly IFetch<Nasa> _fetcher;
public NasaFetcherTransformerDecorator(IFetch<Nasa> fetcher)
{
_fetcher = fetcher;
}
public Nasa Fetch()
{
var result = _fetcher.Fetch();
if (result != null)
{
switch (result.media_type)
{
case "image":
var nasaFetchImageTransformer = new NasaFetchImageTransformer();
nasaFetchImageTransformer.Transform(result);
break;
case "video":
var nasaFetchVideoTransformer = new NasaFetchVideoTransformer();
nasaFetchVideoTransformer.Transform(result);
break;
}
}
return result;
}
}
public class Test
{
public void TestNasaFetcher()
{
var data = new NasaFetcherTransformerDecorator(new NasaFetcher(new HttpClientWrapper()));
var nasa = data.Fetch();
}
}
Related
I have the below classes.
public class Source { public string ConfigData { get; set; } }
public class Configuration { public IEnumerable<IpAddress> IpAddresses { get; set; } }
public class IpAddress
{
public string Start { get; set; }
public string End { get; set; }
public bool IsValid { get; set; }
public Family IPFamily { get; set; }
}
First, I am getting the ConfigData as string from a source and deserializing it below:
var storedIPAddresses = JsonConvert.DeserializeObject<Configuration>( source.ConfigData).IpAddresses;
Next I am doing some checks, which essentially sets the values of IsValid and IPFamily.
if (storedIPAddresses.Any())
{
foreach (var ipDetail in storedIPAddresses)
{
if (!string.IsNullOrEmpty(ipDetail.StartIpAddress) && !string.IsNullOrEmpty(ipDetail.EndIpAddress))
{
if (bla == blabla)
{
ipDetail.IsValid = true;
ipDetail.IPFamily = IPAddressFamily.IPV4;
}
}
}
}
Lastly, I am supposed to return the souce object by chucking in the updated storedIPAddresses inside, which is where I need some guidance.
I am able to do in following way; but looking for any more elegant way?
var config = new Configuration();
config = JsonConvert.DeserializeObject<Configuration>(source.ConfigData);
config.IpAddresses = storedIPAddresses;
source.ConfigData = JsonConvert.SerializeObject(config);
return source;
I'm looking for the best approach of working with different types identically.
I have a web service that goes to specific resource, makes some research and returns an object WebResult, that contains all information about completed operations.
And now I'd like to build a set of different metrics, that will describe all received results. These metrics should provide
different types of data
easy way to collect it
possibility to deserialize it.
Example 1
First I've created separate classes for different metrics
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string ExceptionMessage { get; set; }
public abstract void FillAllMetrics(WebResult result);
}
public class Resource1Statistic : AbstractStatistic
{
public string Title { get; set; }
public string[] Table1_Header { get; set; }
public int Table1_RowCount { get; set; }
public string[] Table2_Header { get; set; }
public int Table2_RowCount { get; set; }
public override void FillAllMetrics(WebResult result)
{
this.Url = result.url;
this.Title = result.data["title"];
this.Table1_Header = result.data["table1.header"].ToObject<string[]>();
//...
}
}
It works, but I'd like to make it in more standard way. One of the reason is that in this approach I have to create separate web form for each metrics.
Example 2
Second working example is universal but redundant: create an abstraction of any datatype
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string Exception { get; set; }
public Dictionary<string, Metric> Metrics { get ;set;}
public abstract void FillAllMetrics(WebResult webResult);
}
public class Metric // Specific class for data
{
public string StringValue { get; set; }
public int? IntegerValue { get; set; }
public string[] ArrayValue { get; set; }
public DateTime? DateTimeValue { get; set; }
}
public class Resource1Statistic : AbstractStatistic
{
public override void FillAllMetrics(WebResult result)
{
this.Metrics.Add("title",
new Metric() { StringValue = result.data["title"].ToString() });
this.Metrics.Add("Table1 Header",
new Metric() { ArrayValue = result.data["table1.header"].ToObject<string[]>() });
//...
}
It works, but I'm sure there is more elegant solution. I don't like to take all these null values in json.
Examples 3
Generic solution (regarding to Adwaenyth)
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string Exception { get; set; }
public List<AbstractMetric> Metrics { get ;set;}
public abstract void FillAllMetrics(WebResult webResult);
}
public abstract class AbstractMetric{}
public class Metric<T> : AbstractMetric
{
public string Name { get; set; }
public T Value { get; set; }
public string Type { get; private set; }
public Metric()
{
this.Type = typeof(T).ToString();
}
}
public class Resource1Statistic : AbstractStatistic
{
public override void FillAllMetrics(WebResult result)
{
this.Metrics.Add(new Metric<string>()
{ Name = "title",
Value = result.data["title"].ToString() });
this.Metrics.Add(new Metric<string[]>()
{ Name = "Table1 Header",
Value = result.data["table1.header"].ToObject<string[]>() });
//...
}
This solution looks nice, but I have to write custom deserializer.
What do you think, is there some good pattern that fits to my task? Or what's the best approach?
I'm new on java/android:
I'm using this list of objects on wp7 and I want pass to android, how I do this:
My big object c#:
public class ListCountries
{
public List<CountriesRepresented> _countriesRepresented { get; set; }
public List<CountriesOrigin > _countriesOrigin { get; set; }
}
My others two objects in c#:
public class CountriesRepresented
{
public int CountryID { get; set; }
public string Designation { get; set; }
public string Symbol { get; set; }
public string NomDesignationISO { get; set; }
}
public class CountriesOrigin
{
public int CountryID { get; set; }
public string Designation { get; set; }
public string Symbol { get; set; }
public string NomDesignationISO { get; set; }
}
My java Deserializer:
public Object[] getListCountries()
{
try {
HttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost(Config.WS_PATH);
post.setHeader("content-type", "application/json; charset=UTF-8");
post.addHeader("Client-Application","3601cfde-e440-4a84-a2cc-a402f4c7bd14");
HttpResponse resp = httpClient.execute(post);
String respStr = EntityUtils.toString(resp.getEntity());
ListCountries _listCountries = new JSONDeserializer().deserialize(ListCountries .class, respStr);
return _listCountries;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
My big object in java:
public class ListCountries {
public List<CountriesRepresented> _CountriesRepresented;
public List<CountriesOrigin > _CountriesOrigin ;
public List<CountriesRepresented> getCountriesRepresented() {
return this._CountriesRepresented;
}
public List<CountriesOrigin > getCountriesOrigin() {
return this._CountriesOrigin ;
}
public void setCountriesRepresented (List<CountriesRepresented> CountriesRepresented) {
this._CountriesRepresented = CountriesRepresented;
}
public void setCountriesOrigin (List<CountriesOrigin > CountriesOrigin ) {
this._CountriesOrigin = CountriesOrigin ;
}
}
My service is on WebAPI and give me an correct answer example: `{"PaisesRepresentantes":[{"PaisID":4,"Designacao":"Alemanha","Sigla":"DEU","NomDesignacaoISO":"GERMANY"},{"PaisID":21,.......
your classes in Java should be something like,
public class ListCountries
{
public List<CountriesRepresented> _countriesRepresented; //Make it private if you want and then you can add getter setter function
public List<CountriesOrigin > _countriesOrigin;
}
public class CountriesRepresented
{
public int CountryID; //Make it private if you want and then you can add getter setter function
public String Designation;
public String Symbol;
public String NomDesignationISO;
}
public class CountriesOrigin
{
public int CountryID; //Make it private if you want and then you can add getter setter function
public String Designation;
public String Symbol;
public String NomDesignationISO;
}
The problem with your existing code is that you have declared variable as _CountriesRepresented rather it should be _countriesRepresented i.e. properties are case sensitive and those are mapped to variables declared in class and do remember to add a default constructor if you add any custom constructor to any of those classes
I have a request like this:
filter[logic]:and
filter[filters][0][value]:a
filter[filters][0][operator]:startswith
filter[filters][0][field]:result
filter[filters][0][ignoreCase]:true
I need to receive it on the Controller but I don't know exactly how. I have tried this view model:
{
public class SearchFilterViewModel
{
public string logic { get; set; }
public List<SearchFilterFiltersViewModel> filters { get; set; }
}
public class SearchFilterFiltersViewModel
{
public string value { get; set; }
//public string operator { get; set; }
public string field { get; set; }
public bool ignoreCase { get; set; }
}
}
But the Controller receives it all null. operator property is commented because operator is a reserved keyword, I don't know how to make Asp.Net to use it. And I don't know if this is the cause of the problem.
Note that I can't change the request body pattern because it comes from this Kendo Widget.
This is my Controller(test version):
public ActionResult Text(SearchFilterViewModel filter)
{
return Json("", JsonRequestBehavior.AllowGet);
}
Here is working solution
Model:
public class SearchFilterViewModel
{
public string logic { get; set; }
public List<SearchFilterFiltersViewModel> filter { get; set; }
}
public class SearchFilterFiltersViewModel
{
public string value { get; set; }
public string oper { get; set; }
public string field { get; set; }
public bool ignoreCase { get; set; }
}
Then you can write custom IValueProvider where you can override usual parsing mechanism like this:
public class KendoValueProvider : NameValueCollectionValueProvider
{
public KendoValueProvider(NameValueCollection originalCollection)
: base(UpdateCollection(originalCollection), CultureInfo.InvariantCulture)
{
}
private static NameValueCollection UpdateCollection(NameValueCollection collection)
{
NameValueCollection result = new NameValueCollection();
foreach (string key in collection.Keys)
{
// ignore all other request
if (!key.StartsWith("filter"))
return null;
var newKey = key
.Replace("[filters]", string.Empty)
.Replace("filter[logic]", "logic")
.Replace("[value]", ".value")
.Replace("[operator]", ".oper")
.Replace("[field]", ".field")
.Replace("[ignoreCase]", ".ignoreCase");
var value = collection[key];
result.Add(newKey, value);
}
return result;
}
}
Then you need to write ValueProviderFactory that will register this ValueProvider like this:
public class KendoValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
return new KendoValueProvider(controllerContext.HttpContext.Request.QueryString);
}
}
And the last step is just register it in Global.asax file
ValueProviderFactories.Factories.Add(new KendoValueProviderFactory());
And sample Action
[HttpGet]
public ActionResult Index(SearchFilterViewModel model)
{
return Json(model, JsonRequestBehavior.AllowGet);
}
I build a rest service which output are json. I using Newtonsoft.Json.
This is my class.
public class DownloadPDA
{
public List<FRUTE> lsRute { get; set; }
public List<FCUSTMST> lsCustomer { get; set; }
public List<FMASTER> lsMaster { get; set; }
public List<FNOTEC> lsNotec { get; set; }
public List<FINFO> lsInfo { get; set; }
public List<FBRAND> lsBrand { get; set; }
public List<FKPL> lsKpl { get; set; }
}
but when I test my rest service my result are:
{"downloadDataResult":"{"lsBrand":[{}],"lsCustomer":[{},{},{}],"lsInfo":[],"lsKpl":null,"lsMaster":[{},{},{},{},{}],"lsNotec":[],"lsRute":[{},{},{}]}"}
it not show the data in list. I know something is wrong. Can anybody help?
This one of my collection class
public class FRUTE
{
private String norute;
private String custno;
private String flag;
private String st_visit;
private float amount;
private int jmlvisit;
public FRUTE() { }
public void getData(DCTRTDTO dto) {
this.norute = dto.NOROUT;
this.custno = dto.NOCUST;
this.flag = dto.FLAG;
this.st_visit = "not yet";
this.amount = 10;
this.jmlvisit = 1;
}
public static List<FRUTE> getList(List<DCTRTDTO> lsRute)
{
List<FRUTE> ls = new List<FRUTE>();
FRUTE info = new FRUTE();
foreach (DCTRTDTO dto in lsRute)
{
info.getData(dto);
ls.Add(info);
}
return ls;
}
}
Your FRUTE class doesn't have public properties that are required for Json serialization.
Encapsulate you private fields and all will work as expected.
public class FRUTE
{
private String norute;
private String custno;
public string Norute
{
get { return norute; }
set { norute = value; }
}
public string Custno
{
get { return custno; }
set { custno = value; }
}
//...
}