I'm doing a little project for school where I try to do a spreadsheet program, and I have two classes, I will be simplifying this with pseudocode a little bit so it's not too messy.
class DocumentController {
Document doc // This is a class with a CRUD on a document (It haves
// Sheets and every Sheet haves a Table full of Cells)
Parser p
getValueOfCell (sheetName, positionX, positionY) {
returns value of a cell in a sheet in the position x,y
}
setCell (String expression, sheetName, positionX, positionY) {
//Somewhere here we need to use p.evaluate()
}
}
class Parser {
DocumentController docController;
evaluate (expression: String) {
//Somewhere here, I need to use method getCell from Document
// for evaluating the expression (The expressions have
// references to other cells so the Parser need to resolve
// these references)
...
return value of the expression (float, integer, string, whatever)
}
}
So apparently my teacher said to me that this is a bad design, because these classes are too coupled and this is a code smell. Can someone explain me why is this so bad? How can I make a better design?
Thank you, sorry if I made some typos or the code is not legible
I think you want something like:
Class Main{
public void main(){
DocumentController dc = new DocumentController();
//you can get ahold of the parser by
Parser p = dc.getParser();
}
}
Class Parser{
DocumentController dc;
public Parser(DocumentController dc){
this.dc = dc;
}
//your methods
}
Class DocumentController{
Parser p;
public DocumentController(){
this.p = new Parser(this);
}
public Parser getParser(){
return this.p;
}
//your methods
}
Although there are probably better ways of doing this instead like passing your object to the method when you need it. Something like
Class Main{
public void main(){
DocumentController dc = new DocumentController();
Parser p = new Parser();
p.myParserMethod(dc);
dc.myDocMethod(p);
}
}
Class Parser{
public myParserMethod(DocumentController dc){
//you can use the same documentController object here
}
}
Class DocumentController{
public myDocMethod(Parser p){
//you can use your parser object here
}
}
hope that helps
It looks like you want to format value by some key expression. If yes, then we can create mapping between this key expression and format classes. Then we can use Factory pattern to create desired objects to format your cell value.
Let me show a simple example via C#.
So this is a DocumentController:
public class DocumentController
{
private DocumentService _documentService;
public DocumentController()
{
_documentService = new DocumentService(); // this dependency can be
// resolved by IoC container
}
public void GetValueCell(int docId, string sheetName, int positionX,
int positionY)
{
_documentService.GetValueCell(docId, sheetName, positionX,
positionY);
}
public void SetCell(int docId, string expression, string sheetName, int
positionX, int positionY, object value)
{
_documentService.SetCell(docId, expression, sheetName, positionX,
positionY, value);
}
}
And this is a service which will execute logic related to Document:
public class DocumentService
{
private DocumentRepository _documentRepository;
public DocumentService()
{
_documentRepository = new DocumentRepository();
}
public string GetValueCell(int docId, string sheetName, int positionX, int positionY)
{
Document document = _documentRepository.GetById(docId);
return document.GetCellValue(sheetName, positionX, positionY);
}
public void SetCell(int docId, string expression, string sheetName, int
positionX, int positionY, object value)
{
Document document = _documentRepository.GetById(docId);
document.SetCellValue(expression, sheetName, positionX, positionY,
value);
}
}
It is unknown how you get Document, but it is possible to use repository pattern for that purpose.
public class DocumentRepository
{
public Document GetById(int id) { throw new NotImplementedException(); }
}
and this is a Document class:
public class Document
{
private object[][] _cells;
public Document(int x)
{
_cells = new object[x][];
}
public string GetCellValue(string sheetName, int positionX, int positionY)
{
return string.Empty;
}
public void SetCellValue(string expression, string sheetName, int
positionX, int positionY, object value)
{
FormatterType formatterType = new
FormatterTypeToExpression().FormatterByExpression[expression];
Formatter formatter = new FormatterFactory().
FormatterByFormatterType[formatterType];
object formattedCell = formatter.Format(value);
_cells[positionX][positionY] = formattedCell;
}
}
and this is a mapping between FormatterType and your key expression:
public class FormatterTypeToExpression
{
public Dictionary<string, FormatterType> FormatterByExpression { get; set; } =
new Dictionary<string, FormatterType>
{
{ "string", FormatterType.String}
// here you write expressions and foramtters
};
}
This is a formatter type:
public enum FormatterType
{
String, Number, Decimal, Whatever
}
Then you need something like factory to take a formatter:
public abstract class Formatter
{
public abstract object Format(object value);
}
And abstract class which will define behavior of derived formatter classes:
public class FormatterString : Formatter
{
public override object Format(object value)
{
return "I am a formatted string value";
}
}
An example how FormatterFactory could look like:
public class FormatterFactory
{
public Dictionary<FormatterType, Formatter> FormatterByFormatterType { get; set; }
= new Dictionary<FormatterType, Formatter>
{
{ FormatterType.String, new FormatterString()}
// here you write FormatterType and formatters
};
}
I upgraded my spring stream from 1.3.0 to 2.1.2 and the default serializer was changed from Kyro (deprecated) into Jackson.
I have a kafka topic that more than one type of messages can be sent to. With Kyro I used to deserialize it into Object.class and then cast it to the relevant type of class.
With jackson I can't achieve this functionality, because I have to specify the type of class I want to deserialize to in advance, otherwise, it's been deserialized into a string.
I tried to find an example but couldn't find anything. Any ideas how can I achieve the same functionality? I want to make it as efficient as possible.
You can add hints to the Jackson encoding so it is decoded to the right concrete type:
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#class")
#SpringBootApplication
#EnableBinding(Processor.class)
public class So56753956Application {
public static void main(String[] args) {
SpringApplication.run(So56753956Application.class, args);
}
#StreamListener(Processor.INPUT)
public void listen(Foo foo) {
System.out.println(foo);
}
#Bean
public ApplicationRunner runner(MessageChannel output) {
return args -> {
output.send(new GenericMessage<>(new Bar("fiz")));
output.send(new GenericMessage<>(new Baz("buz")));
};
}
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#class")
public static abstract class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return getClass().getName() + " [bar=" + this.bar + "]";
}
}
public static class Bar extends Foo {
public Bar() {
super();
}
public Bar(String bar) {
super(bar);
}
}
public static class Baz extends Foo {
public Baz() {
super();
}
public Baz(String bar) {
super(bar);
}
}
}
and
com.example.So56753956Application$Bar [bar=fiz]
com.example.So56753956Application$Baz [bar=buz]
See here.
You can still use Kryo if you want. You can just add it manually using #StreamMessageConverter- https://cloud.spring.io/spring-cloud-stream/spring-cloud-stream.html#spring-cloud-stream-overview-user-defined-message-converters.
With regard to "With jackson I can't achieve this functionality, because I have to specify the type of class. . ." - that is not accurate since the type of the class gets picked up from the signature of the handler method and it is transparent to you as a user.
is there a way to map a DTO using MatStruct which have a few final data members as well and cannot have a default constructor , like :
public class TestDto {
private final String testName;
private int id;
private String testCase;
public TestDto(String testName) {
this.testName = testName;
}
public String getTestName() {
return testName;
}
public int getId() {
return id;
}
public String getTestCase() {
return testCase;
}
public void setId(int id) {
this.id = id;
}
public void setTestCase(String testCase) {
this.testCase = testCase;
}
}
please suggest how could this DTO be mapped using MapStruct.
You can use #ObjectFactory that would construct an instance of your DTO.
For example:
#Mapper
public interface MyMapper {
#ObjectFactory
default TestDto create() {
return new TestDto("My Test Name");
}
//the rest of the mappings
}
You can also enhance the #ObjectFactory to accept the source parameter, that you can use to construct the TestDto. You can even use a #Context as an Object Factory.
NB: You don't have to put the #ObjectFactory method in the same Mapper, or even a MapStruct #Mapper. You can put it in any class (or make it static) and then #Mapper(uses = MyFactory.class)
I have a simple polymorphic model like this
public class Foo {
private Bar bar1;
private Bar bar2;
public Bar getBar1() {
return bar1;
}
public Bar getBar2() {
return bar2;
}
public void setBar1(Bar bar1) {
this.bar1 = bar1;
}
public void setBar2(Bar bar2) {
this.bar2 = bar2;
}
}
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#type")
public class Bar {
}
public class BarExpression extends Bar {
private String expression;
public String getExpression() {
return expression;
}
#JsonIgnore
public Object getValue() {
return null;
}
public void setExpression(String expression) {
this.expression = expression;
}
}
public class BarLiteral extends Bar {
private String value;
private String type;
public String getType() {
return type;
}
public Object getValue() {
return value;
}
public void setType(String type) {
this.type = type;
}
public void setValue(String value) {
this.value = value;
}
}
Serializing a simple example like this
public void run() throws Exception {
Foo foo;
BarLiteral bar1;
BarExpression bar2;
//
foo = new Foo();
bar1 = new BarLiteral();
bar1.setType("java.lang.String");
bar1.setValue("gnu");
foo.setBar1(bar1);
bar2 = new BarExpression();
bar2.setExpression("bean.property * 2");
foo.setBar2(bar2);
//
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
StringWriter w = new StringWriter();
mapper.writeValue(w, foo);
System.out.println(w.toString());
}
gives the expected result:
{
"bar1" : {
"#type" : "de.mit.jackson.BarLiteral",
"value" : "gnu",
"type" : "java.lang.String"
},
"bar2" : {
"#type" : "de.mit.jackson.BarExpression",
"expression" : "bean.property * 2"
}
}
The question is now: I want to improve user experience when handwriting this "DSL" by supporting "primitive shortcuts" for the typed "Bar..." classes like this
{
"bar1" : "gnu",
"bar2" : "#{bean.property * 2}"
}
The solution that came closest was using a converter on Foo#bar1 and Foo#bar2, checking for either String or "Bar" input, but this solution requires decoration of every attribute definition.
Creating a deserializer with a comparable behavior did not work, as the #JsonTypeInfo is not compatible in the sense that i can have a #JsonDeserialize implementation that will check for a String event and then delegate to the standard #JsonTypeInfo process. The #JsonTypeInfo standard will check only for the #type and then delegate back to the (subtype) deserializer which is again my wrapper implementation.
The required process is
if input event is string {
parse and return string input
} else {
activate #type parsing delegate;
after #type parsing activate standard BeanDeserializer
(**not** my implementation)
}
Is there another hook i am missing?
I am trying to access the object of my RepositoryJournalVoucher.cs class from my main class named RepositoryVoucher.cs but i am unable to access or make instance, dont know why.
abstract class RepositoryUniversalAccounts.cs
namespace eCartDynamic.RepositoryApplication
{
public abstract class RepositoryUniversalAccounts
{
public string AccountCode { get; set; }
public abstract string PostVoucher();
}
}
RepositoryCashVoucher.cs
namespace eCartDynamic.RepositoryApplication
{
public class RepositoryCashVoucher : RepositoryUniversalAccounts
{
public override string PostVoucher()
{
AccountCode = "16-01-001-01-000001-CV";
return "CV Posted Sucessfully";
}
}
}
RepositoryJournalVoucher.cs
namespace eCartDynamic.RepositoryApplication
{
public class RepositoryJournalVoucher : RepositoryUniversalAccounts
{
public override string PostVoucher()
{
AccountCode = "16-01-001-01-000001-JV";
return "JV Posted Sucessfully";
}
}
}
Main Class. RepositoryVoucher.cs
namespace eCartDynamic.RepositoryApplication
{
public class RepositoryVoucher
{
RepositoryUniversalAccounts abc = new RepositoryJournalVoucher();
}
}
in my RepositoryVoucher.cs Class i am not be able to make object of anything like:
RepositoryUniversalAccounts abc = new RepositoryJournalVoucher();
RepositoryJournalVoucherabc obj = new RepositoryJournalVoucher();