This is a link to my application that actually runs on the design specified in the image.
The idea behind the image is that the SubjectInfoViewer
behaves as the context in the strategy pattern, and the ViewingQueryComponent
behaves as the strategy interface, as well as the component class in the decorator pattern, and its implementing classes except the Filter class are the different strategies. These strategies return an SQL string specific to their description, which can be decorated using the Filter class, which is the decorator, in the decorator pattern. The classes implementing the filter class just append the where clause of the SQL string produced by the strategies and push the parameters involved in the where clause in a parameter stack, so they can finally by used by a parameterized SQL statement involving the whole "stitched" SQL string.
For some reason, this doesn't seem to be the right approach to tackle this particular usecase. Please suggest the best practices used in this situation.
The reason this doesn't seem to be the right approach is that there are a lot of tables being joined in the base queries, just for the convenience of writing where clauses in the Filters.
I really would like to reduce the accidental complexity of having to push the parameters used in where clauses into the paramStack
for using them in a parameterized query and having to call all the filters only for some filters to do nothing but by pass the call to another filter without doing anything useful.
By the way, the repeated code in the base queries can be easily abstracted..
Also, better decoupling can be ensured by isolating the UserInterface
reference to the SubjectInfoViewer
so that it passes the parameters it receives from the getters of the UserInterace
to the Filters, instead of having the Filters get them from the UserInterface
.
SubjectInfoViewer.java
public class SubjectInfoViewer {
private ViewingQueryComponent baseQuery;
private ViewingQueryComponent vqc;
private String viewingQuery;
private Stack<String> viewParams=new Stack<>();
private UserInterface ui;
public void changeBaseQuery(ViewingQueryComponent vqc){
baseQuery=vqc;
}
public void stitch(){
vqc=new COFilter(ui,viewParams,
new USNFilter(ui,viewParams,
new DifficultyFilter(ui,viewParams,
new SectionFilter(ui,viewParams,
new DateFilter(ui,viewParams,
new ModuleFilter(ui,viewParams,
new SubjectFilter(ui,viewParams,
baseQuery)))))));
viewingQuery=vqc.stitch()+" GROUP BY STATUS.Topic_Name,QUESTION_BANK.Question_Statement";
}
public SubjectInfoViewer(UserInterface ui) {
this.ui=ui;
baseQuery=new DefaultViewingQuery();
}
public DefaultTableModel getTableModel() {
return new DBGateway().getSubjectDetails(viewingQuery,viewParams);
}
}
ViewingQueryComponent.java
public interface ViewingQueryComponent {
String stitch();}
DefaultViewingQuery.java
public class DefaultViewingQuery implements ViewingQueryComponent {
private String sql=
"SELECT "
+ "TOPICS.Topic_Name AS \"Topic Name\", "
+ "TOPICS.Textbook_Name AS \"Textbook Name\", "
+ "TOPICS.Page_Number AS \"Page Number\", "
+ "MADE_FROM.Question_Statement AS \"Question Statement\", "
+ "QUESTION_BANK.Total_Marks AS \"Total Marks\", "
+ "ROUND((COUNT(DISTINCT STATUS.USN)/(SELECT SUM(STUDENT.USN) FROM STUDENT))*100,2) AS \"Total Students (%)\" "
+ "FROM "
+ "STATUS, "
+ "TEXTBOOK, "
+ "SUBJECT, "
+ "STUDENT, "
+ "DISTRIBUTE, "
+ "TOPICS LEFT JOIN (MADE_FROM,QUESTION_BANK) ON TOPICS.Topic_Name = MADE_FROM.Topic_Name AND QUESTION_BANK.Question_Statement=MADE_FROM.Question_Statement "
+ "WHERE "
+ "DISTRIBUTE.Topic_Name=TOPICS.Topic_Name and "
+ "TEXTBOOK.Textbook_Name=TOPICS.Textbook_Name and "
+ "STATUS.Topic_Name=TOPICS.Topic_Name and "
+ "STATUS.USN=STUDENT.USN ";
@Override
public String stitch() {
return sql;
}}
SectionViewingQuery.java
public class SectionViewingQuery implements ViewingQueryComponent{
private String sql;
public SectionViewingQuery(UserInterface ui){
sql= "SELECT "
+ "TOPICS.Topic_Name AS \"Topic Name\", "
+ "TOPICS.Textbook_Name AS \"Textbook Name\", "
+ "TOPICS.Page_Number AS \"Page Number\", "
+ "MADE_FROM.Question_Statement AS \"Question Statement\", "
+ "QUESTION_BANK.Total_Marks AS \"Total Marks\", "
+ "(COUNT(DISTINCT STATUS.USN)/(SELECT SUM(STUDENT.USN) FROM STUDENT WHERE STUDENT.Section=\""+ui.getSection()+"\"))*100 AS \"Total Students (%)\" "
+ "FROM "
+ "STATUS, "
+ "TEXTBOOK, "
+ "SUBJECT, "
+ "STUDENT, "
+ "DISTRIBUTE, "
+ "TOPICS LEFT JOIN (MADE_FROM,QUESTION_BANK) ON TOPICS.Topic_Name = MADE_FROM.Topic_Name AND QUESTION_BANK.Question_Statement=MADE_FROM.Question_Statement "
+ "WHERE "
+ "DISTRIBUTE.Topic_Name=TOPICS.Topic_Name and "
+ "TEXTBOOK.Textbook_Name=TOPICS.Textbook_Name and "
+ "STATUS.Topic_Name=TOPICS.Topic_Name and "
+ "STATUS.USN=STUDENT.USN ";
}
@Override
public String stitch() {
return sql;
}}
Filter.java
public abstract class Filter implements ViewingQueryComponent {
private ViewingQueryComponent vqc;
protected Stack<String> paramStack;
protected String sql="";
abstract boolean setSql();
Filter(ViewingQueryComponent vqc,Stack<String> paramStack){
this.paramStack=paramStack;
this.vqc=vqc;
}
@Override
public String stitch(){
if(setSql())
return vqc.stitch()+" and "+sql;
return vqc.stitch()+sql;
}}
SubjectFilter.java
public class SubjectFilter extends Filter{
String subject;
public SubjectFilter(UserInterface ui,Stack<String> paramStack,ViewingQueryComponent vqc) {
super(vqc,paramStack);
subject=ui.getSubject();
}
@Override
boolean setSql() {
if(!CheckHelper.checkEmpty(subject)){
sql=" TEXTBOOK.Subject_Name=? ";
paramStack.push(subject);
return true;
}return false;
}}